rest-framework源碼部分梳理

dispatch函數是源碼的入口,從這裏開始梳理django

下面演示如何使用rest_framework的用戶認證功能,以及爲何要這樣作api

1.寫路由匹配app

  由於是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風格視圖ide

複製代碼
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函數:post

複製代碼
    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初識化這兩個地方this

1.1先看從新封裝request這個url

複製代碼
    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.2spa

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')
複製代碼
相關文章
相關標籤/搜索