【问题标题】:DRF APIView move request validation to dispatch method using request.dataDRF APIView 移动请求验证以使用 request.data 调度方法
【发布时间】:2016-11-20 07:40:03
【问题描述】:

我创建了一个基本 api 视图,它从 APIView 扩展而来,我在其中记录响应时间、记录请求和其他常见内容。

现在,我还想在这里添加请求验证,使用子类 Views 中定义的 Serializer。我认为合适的地方是把它放在dispatch() 方法中。但是在我调用API.dispatch() 方法之前,request.data 并没有准备好。所以,那是行不通的。有人可以在正确的方向上帮助我如何将验证移动到一个地方吗?

这是类结构:

class BaseView(APIView):
    validation_serializer = None

    def dispatch(self, request, *args, **kwargs):
        # Some code here
        # How to use `validation_serializer` here, to validate request data?
        # `request.data` is not available here.
        response = super(BaseView, self).dispatch(request, *args, **kwargs)
        # Some code here
        return response

class MyView(BaseView):
    validation_serializer = ViewValidationSerializer

    def post(self, request, *args, **kwargs):
        pass

我认为另一种方法是在 post() 方法的顶部使用装饰器。但是,如果只有一种更简洁的方法,而不是在整个项目中放置装饰器?

注意:类似于这里的问题:Django - DRF - dispatch method flow。但根据那里的建议,我不想只是从 DRF 源代码中复制整个 dispatch 方法。

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    将django请求处理成DRF请求(并添加request.data属性)的方法是APIView.initialize_request APIView.dispatch() method calls it 然后继续调用适当的方法处理程序 (post/patch/put)。

    您可以尝试通过调用它并使用返回的对象来自己执行此操作:

    class BaseView(APIView):
        validation_serializer = None
    
        def dispatch(self, request, *args, **kwargs):
            request = self.initialize_request(request, *args, **kwargs)
            kwargs['context'] = self.get_serializer_context()
            serializer = self.validation_serializer(data=request.data, *args, **kwargs)
    
            # use `raise_exception=True` to raise a ValidationError
            serializer.is_valid(raise_exception=True)
    
            response = super(BaseView, self).dispatch(request, *args, **kwargs)
            return response
    

    但是,我建议不要这样做,因为dispatch() 的其他功能可能应该在处理验证之前执行;因此,您可以将上述逻辑移至相关的 post/patch/put 方法中。

    在这些方法中你也可以直接使用self.request,因为它已经被dispatch()初始化了。

    【讨论】:

      【解决方案2】:

      我认为drf-tracking 可以满足您的需求。您可能想检查一下。

      【讨论】:

      • 看起来很有趣。但它用于记录请求详细信息。我想要的是验证的可能性。
      【解决方案3】:

      我认为您的处理方式不正确。使用验证记录请求的最佳方式是在您的身份验证类中并将审核日志添加到请求中。

      然后您可以使用您的APIView 记录渲染时间,对照在身份验证类中生成的AuditLog


      这是一个使用令牌认证的例子,假设每个请求都有一个标头Authorization: Bearer <Token>

      settings.py

      ...
      
      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': (
              'common.authentication.MyTokenAuthenticationClass'
          ),
          ...,
      }
      

      common/authentication.py

      from django.utils import timezone
      from django.utils.translation import ugettext_lazy as _
      
      from ipware.ip import get_real_ip
      from rest_framework import authentication
      from rest_framework import exceptions
      
      from accounts.models import Token, AuditLog
      
      
      class MyTokenAuthenticationClass(authentication.BaseAuthentication):
      
          def authenticate(self, request):
      
              # Grab the Athorization Header from the HTTP Request
              auth = authentication.get_authorization_header(request).split()
      
              if not auth or auth[0].lower() != b'bearer':
                  return None
      
              # Check that Token header is properly formatted and present, raise errors if not
              if len(auth) == 1:
                  msg = _('Invalid token header. No credentials provided.')
                  raise exceptions.AuthenticationFailed(msg)
              elif len(auth) > 2:
                  msg = _('Invalid token header. Credentials string should not contain spaces.')
                  raise exceptions.AuthenticationFailed(msg)
      
              try:
                  token = Token.objects.get(token=auth[1])
                  # Using the `ipware.ip` module to get the real IP (if hosted on ElasticBeanstalk or Heroku)
                  token.last_ip = get_real_ip(request)
                  token.last_login = timezone.now()
                  token.save()
      
                  # Add the saved token instance to the request context
                  request.token = token
      
              except Token.DoesNotExist:
                  raise exceptions.AuthenticationFailed('Invalid token.')
      
              # At this point, insert the Log into your AuditLog table and add to request:
              request.audit_log = AuditLog.objects.create(
                  user_id=token.user,
                  request_payload=request.body,
                  # Additional fields
                  ...
              )
      
              # Return the Authenticated User associated with the Token
              return (token.user, token)
      

      现在,您可以访问请求中的 AuditLog。因此,您可以在验证前后记录所有内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-06
        • 1970-01-01
        • 2018-06-12
        • 1970-01-01
        • 1970-01-01
        • 2022-06-25
        相关资源
        最近更新 更多