• rest-framework源码部分梳理


    dispatch函数是源码的入口,从这里开始梳理

    下面演示如何使用rest_framework的用户认证功能,以及为什么要这样做

    1.写路由匹配

      因为是cbv视图,所以用as_view()  

    复制代码
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^test/', views.TestView.as_view()),
    ]
    复制代码

    2.CBV风格视图

    复制代码
    from django.shortcuts import HttpResponse
    
    from rest_framework.views import APIView#导入rest_framework的试图类
    
    class TestView(APIView):
      self.dispatch      #只是为了进源码
    def get(self,request,*args,**kwargs): return HttpResponse('ok')
    复制代码

    最基本的框架就这样搭好了,开始看源码,源码的入口是在APIView 的dispatch方法,点进去

    dispatch函数:

    复制代码
        def dispatch(self, request, *args, **kwargs):#这是APIView类的入口
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            和Django的dispatch类似,但是增加了额外的钩子,比如开始,结束,以及异常的处理
            """
            self.args = args    #封装参数
            self.kwargs = kwargs
            request = self.initialize_request(request, *args, **kwargs)     #重新封装了request
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    复制代码

    这个函数里面,比较重要的地方就是重新封装request和self.initial初识化这两个地方

    1.1先看重新封装request这个

    复制代码
        def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            返回初始化后的request
            """
            parser_context = self.get_parser_context(request)
    
            return Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(),       #重点关注这个,看名字就应该找到这个,继续点进去
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context          
            )  
    复制代码

    1.2

    def get_authenticators(self):
            """
            Instantiates and returns the list of authenticators that this view can use.
            """
            return [auth() for auth in self.authentication_classes] #从配置读取用户认证配置,然后实例化,点进这个self.authentication_classes
    
    

    这个函数返回了一个列表,通过列表生成式,循环    self.authentication_classes   ,然后拿到的东西实例化

    tips:看到一个东西加括号,就两种情况,一种是函数,一种是类,区分函数和类,就看加括号之后的东西,是否还调用属性或者方法

    1.3

    复制代码
    class APIView(View):
    
        # The following policies may be set at either globally, or per-view.
        renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES        
        parser_classes = api_settings.DEFAULT_PARSER_CLASSES
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES      #
        throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    复制代码

    这不能点了,看settings,说明这是配置信息,现在我们拿到的信息是:

      在封装request的时候, 把一个auth()列表传到了request的参数里面,然后这个auth()是从这个self.authentication_classes 里面拿到的

    现在我们可以打印一下这个  self.authentication_classes,self就是APIview 嘛,我门的视图函数就是继承了这个类啊,所以,打印一下:

    拿到了这个:

    [<class 'rest_framework.authentication.SessionAuthentication'>, <class 'rest_framework.authentication.BasicAuthentication'>]

    验证了self.authentication_classes  就是个列表,然后里面是一个一个类

    如果我们自己写这个authentication_classes的话,也要写成一个列表,里面是一个个类,下面看一下这个类要怎么写

    1.4

    然后我们先导入一下这个路径,看一下里面写的啥,

    from rest_framework.authentication import SessionAuthentication

    点进去

    是一个新的文件

     总体看一下,这个文件有这样几个类

    然后都继承了BaseAuthentication类,先看一下这个类

    1.5

    复制代码
    class BaseAuthentication(object):
        """
        All authentication classes should extend BaseAuthentication.
        """
    
        def authenticate(self, request):
            """
            Authenticate the request and return a two-tuple of (user, token).
            """
            raise NotImplementedError(".authenticate() must be overridden.")
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            pass
    复制代码

    没有什么实质性的东西,但是规定了继承这个类的子类,必须要有这个   authenticate   方法,否则就会报错,所以我们就知道了应该怎么自己写这个类了

    那就可以猜到,下面继承了这个基类的几个子类就是不同的认证方法了

    看了这个基类,大概知道自己定义的类里面必须要写这样一个authenticate   方法,不写会报错,但是应该怎么写呢?需要返回什么呢,其实重点是要返回什么

    继续:

    2.1

    复制代码
        def initial(self, request, *args, **kwargs):
            """
            Runs anything that needs to occur prior to calling the method handler.
            """
            self.format_kwarg = self.get_format_suffix(**kwargs)
    
            # Perform content negotiation and store the accepted info on the request
           
    
            neg = self.perform_content_negotiation(request) 
            request.accepted_renderer, request.accepted_media_type = neg
    
            # Determine the API version, if versioning is in use.
         
            version, scheme = self.determine_version(request, *args, **kwargs)
            request.version, request.versioning_scheme = version, scheme 
    
            # Ensure that the incoming request is permitted
            #用户认证
            self.perform_authentication(request)
          
            self.check_permissions(request)
          
            self.check_throttles(request)
    复制代码

     这里的:  self.perform_authentication(request)      -------执行认证

    注意这里传了参数,是封装后的request

    2.2

    复制代码
        def perform_authentication(self, request):
            """
            Perform authentication on the incoming request.
    
            Note that if you override this and simply 'pass', then authentication
            will instead be performed lazily, the first time either
            `request.user` or `request.auth` is accessed.
            """
            request.user
    复制代码

    就执行了一个request.user,这个user应该是个方法,才能直接这么用,所以再找request里面的user方法,

    2.3

    复制代码
        @property
        def user(self):
            """
            Returns the user associated with the current request, as authenticated
            by the authentication classes provided to the request.
            """
            if not hasattr(self, '_user'):
                self._authenticate()
            return self._user
    复制代码

    这里是一个加了装饰器的方法,我们的request里面没有这个_user属性,所以执行的是   self._authenticate()  这个方法

    2.4

    复制代码
        def _authenticate(self):
            """
            Attempt to authenticate the request using each authentication instance
            in turn.
            """
            for authenticator in self.authenticators:    #
                try:
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    self.user, self.auth = user_auth_tuple
                    return
    
            self._not_authenticated()
    复制代码

    循环   self.authenticators,拿到   authenticator 并执行 authenticator 里面的  authenticate 方法,参数为request

    注意, self.authenticators  这个东西在1.1中就已经拿到了,这个就是我们要写的验证的类,然后跟我们得到的结论也一样,有一个authenticate 方法

    2.5 看看这个方法,authenticate

        def authenticate(self, request):
            return (self.force_user, self.force_token)

    返回一个元组,大概就是  用户和token,现在这两行代码是如果有指定值的时候的情况,但是可以说明问题

    再看看1.4里面那几个验证的子类,基本上就确认是返回一个已通过认证的用户和对应的token

    这是通过认证的情况

    2.6再看2.4里面,最后那个     self._not_authenticated()   ,是表示未通过验证的情况

    点进去

    复制代码
        def _not_authenticated(self):
            """
            Set authenticator, user & authtoken representing an unauthenticated request.
    
            Defaults are None, AnonymousUser & None.
            """
            self._authenticator = None
    
            if api_settings.UNAUTHENTICATED_USER:
                self.user = api_settings.UNAUTHENTICATED_USER()
            else:
                self.user = None
    
            if api_settings.UNAUTHENTICATED_TOKEN:
                self.auth = api_settings.UNAUTHENTICATED_TOKEN()
            else:
                self.auth = None
    复制代码

    没有默认值的话,是返回None的,但是是有默认值的

    点进去api_settings ,是个类

    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

    再点进去,就是rest_framework的配置文件

    搜一下,就可以看到这条配置

    'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
     'UNAUTHENTICATED_TOKEN': None,

    默认是调用Django的匿名用户类,所以默认如果不登录,是匿名用户

    还能说明一个问题,不登录也能访问,即匿名用户访问是否被允许,就可以在认证类中配置了,返回None,就是允许,抛出异常就是不允许

    完整的代码

    复制代码
    from django.shortcuts import HttpResponse
    
    from rest_framework.views import APIView#导入rest_framework的试图类
    
    from rest_framework.authentication import BaseAuthentication
    from rest_framework import exceptions
    from . import models
    class CustomAuthenticate(BaseAuthentication):
        """
        All authentication classes should extend BaseAuthentication.
        """
    
        def authenticate(self, request):
            """
            Authenticate the request and return a two-tuple of (user, token).
            """
            tk = request.query_param.get('tk')      #获取用户输入的token   简化写法,就直接在url中传参了
            token_obj = models.Token.objects.filter(token=tk).first()
            if token_obj:
                return (token_obj.user,token_obj)
    
    
            raise exceptions.AuthenticationFailed('请登录')    #抛出异常表示不允许匿名用户访问
    
            # else:
            #     return (None,None)
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            pass
    class TestView(APIView):
        authentication_classes = [CustomAuthenticate,]          #应用认证配置类
        def get(self,request,*args,**kwargs):
            return HttpResponse('ok')
    复制代码
     
     
  • 相关阅读:
    CSS性能让JavaScript变慢?
    Cordova优缺点与环境部署
    nodeapi
    git常用命令
    常见状态码
    关于拉萨
    英语学习
    SQL 按表中的一个int值拆分成对应的个数的记录条数
    SQL分组编号
    C#四舍五入
  • 原文地址:https://www.cnblogs.com/sxh-myblogs/p/8419250.html
Copyright © 2020-2023  润新知