简体   繁体   English

DRF APIView使用request.data将请求验证移动到调度方法

[英]DRF APIView move request validation to dispatch method using request.data

I've created a base api view, which extends from APIView , where I log response time, log request, and other common stuffs. 我已经创建了一个基本api视图,它从APIView扩展,在那里我记录响应时间,日志请求和其他常见的东西。

Now, I also want to add request validation here, using the Serializer defined in sub-class Views. 现在,我还想在这里添加请求验证,使用子类Views中定义的Serializer。 I thought the appropriate place is to put that in dispatch() method. 我认为适当的地方是把它放在dispatch()方法中。 But before I call API.dispatch() method, request.data is not prepared. 但在我调用API.dispatch()方法之前,没有准备request.data So, that won't work. 所以,那是行不通的。 Can someone help me in right direction as to how to move validation to a single place? 有人可以帮我正确指导如何将验证移到一个地方吗?

Here's the class structure: 这是类结构:

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

I thought another approach could be use decorator on the top of post() method. 我认为另一种方法可能是在post()方法的顶部使用装饰器。 But if only there was an cleaner way, than putting decorators all across the project? 但是,如果只有一个更清洁的方式,而不是整个项目的装饰器?

Note: It's similar to the question here: Django - DRF - dispatch method flow . 注意:它类似于这里的问题: Django - DRF - 调度方法流程 But as per the suggestion there, I don't want to just copy the entire dispatch method from DRF source code. 但根据那里的建议,我不想只是从DRF源代码复制整个dispatch方法。

The method that processes the django request into a DRF request (and adds the request.data property) is the APIView.initialize_request . 将django请求处理为DRF请求(并添加request.data属性)的方法是APIView.initialize_request The APIView.dispatch() method calls it and then proceeds to call the appropriate method handler ( post/patch/put ). APIView.dispatch()方法调用它然后继续调用适当的方法处理程序( post / patch / put )。

You can try to do that yourself by calling it and using the returned object: 您可以尝试通过调用它并使用返回的对象来自己完成:

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

However, I would suggest against this, as other functionality of dispatch() probably should be performed prior to handling validation; 但是,我建议不要这样做,因为dispatch()其他功能可能应该在处理验证之前执行; thus, you could moving the above logic to the relevant post/patch/put methods instead. 因此,您可以将上述逻辑移至相关的post / patch / put方法。

In these methods you can also use self.request directly since it was already initialized by dispatch() . 在这些方法中,您也可以直接使用self.request ,因为它已经由dispatch()初始化。

I think drf-tracking does what you are looking for. 我认为drf-tracking可以满足您的需求。 You may want to check it out. 你可能想看一下。

I don't think you're going about this the correct way. 我不认为你这是正确的方式。 The best way to log the request, with validation is in your Authentication Class and add the audit log to the request. 通过验证记录请求的最佳方法是在您的身份验证类中,并将审核日志添加到请求中。

Then you can use your APIView to log the render time, against the AuditLog generated in the Authentication Class. 然后,您可以使用您的APIView记录渲染时间,对照在Authentication Class中生成的AuditLog


Here's an example using Token Authentication, assuming each request has a header Authorization: Bearer <Token> . 这是使用令牌认证的示例,假设每个请求都有一个头部Authorization: Bearer <Token>

settings.py settings.py

...

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

common/authentication.py 通用/ 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)

Now, you have access to the AuditLog in your request. 现在,您可以在请求中访问AuditLog。 So you can log everything before and after validation. 因此,您可以在验证前后记录所有内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM