一、基本流程举例:
urlpatterns = [ url(r‘^admin/‘, admin.site.urls), url(r‘^users/‘, views.HostView.as_view()), ]
from rest_framework.views import APIView from rest_framework.response import Response class HostView(APIView): def dispatch(self, request, *args, **kwargs): """ 请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能 """ return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): return Response(‘GET请求,响应内容‘) def post(self, request, *args, **kwargs): return Response(‘POST请求,响应内容‘) def put(self, request, *args, **kwargs): return Response(‘PUT请求,响应内容‘)
二、源码分析:
当我们发送请求后,执行views里面对应的方法时,最开始执行的是dispatch方法
自己定义的dispatch方法(当然自己可以不定义直接应用APIView里面的就可以了,默认就是这种):
def dispatch(self, request, *args, **kwargs): """ 请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能 """ return super().dispatch(request, *args, **kwargs)
APIView中的dispatch方法(源码):
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django‘s regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #1.将原来的request进行加工,增加了一些功能,将其放入restframework的Request ‘‘‘ request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ‘‘‘
# 原来request对象,django.core.handlers.wsgi.WSGIRequest
# 现在的request对象,rest_framework.request.Request
request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: #2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制 self.initial(request, *args, **kwargs) # Get the appropriate handler method #3.据用户提交的请求方法利用反射获取请求方法 #http_method_names=[‘get‘, ‘post‘, ‘put‘, ‘patch‘, ‘delete‘, ‘head‘, ‘options‘, ‘trace‘] 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) #4.将处理后的response包装 self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
1.将原来的request进行加工,增加了一些功能,将其放入restframework的Request
request = self.initialize_request(request, *args, **kwargs)
进入initialize_request函数后:
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ 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 )
将这些东西前部都封装在rest_framework的Resquest中,并返回她的对象request,从这以后我们调用的request对象不再是Django提供的request对象了,而是APIView的Resquest的对象,2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制
self.initial(request, *args, **kwargs)
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. #2.1处理版本信息 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #2.2处理认证信息 self.perform_authentication(request) #2.3处理权限信息 self.check_permissions(request) #2.4对用户的访问频率进行限制 self.check_throttles(request)
3.据用户提交的请求方法利用反射获取请求方法,并改用请求方法实现其具体功能
4.将处理后的response进行包装
三、处理认证的具体分析
当diapatch方法进行到第一步时,我们调用了initialize_request方法将request等封装在Request中顶返回其对象,
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ 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 )
authenticators=self.get_authenticators() #用于用户认证 是一个由authentication对象组成的列表
def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ return [auth() for auth in self.authentication_classes]
authentication_classes为一个authentication类组成的列表,他默认是调用
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
当然我们一般是自己定义或者配置到settings中,至此我们的得到authentication对象的列表,其封装在Request对象中,
接的执行第二步
#2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制 self.initial(request, *args, **kwargs)
#2.2处理认证信息 self.perform_authentication(request)
查看perform_authentication的源码如下
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
其调用了rest_framework中Request的user方法(这个方法肯定别@property装饰,不然的话不可能直接不加括号的调用)
from rest_framework.request import Request
Request类中的user方法
@property def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ #判断当前类中是否有已经认证过的user if not hasattr(self, ‘_user‘): #没有认证则去认证 self._authenticate() #认证过了直接返回 return self._user
注意:user中的self代表的是request对象
没认证的话执行 调用Request类中的_authenticate()方法
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ #遍历request对象中封装的Authentication对象 for authenticator in self.authenticators: try: #调用Authentication对象中的authenticate方法,必须要有这个方法不然抛出异常 #当然Authentication类一般有我们自己定义,实现这个方法就可以了 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()
认证实例:
局部认证:直接类中定义Authentication类
from django.db import models # Create your models here. class Userinfo(models.Model): name=models.CharField(max_length=32,verbose_name=‘用户名‘) pwd=models.CharField(max_length=32,verbose_name=‘密码‘) token=models.CharField(max_length=64,null=True) def __str__(self): return self.name
urlpatterns = [ url(r‘^admin/‘, admin.site.urls), url(r‘^users/‘, views.HostView.as_view()), url(r‘^auth/‘, views.AuthView.as_view()), ]
import json import hashlib import time from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import BaseAuthentication from rest_framework.request import Request from rest_framework.exceptions import APIException from rest_framework.response import Response from app01 import models # Create your views here. class MyAuthentication(object): def authenticate(self, request): token = request.query_params.get(‘token‘) user=models.Userinfo.objects.filter(token=token).first() if user: return (user.name, ‘aaaaaa‘) raise APIException(‘认证失败‘) class AuthView(APIView): # 设置为空标识不用认证 authentication_classes = [] def get(self, request, *args, **kwargs): dic = {‘code‘: 1000, ‘msg‘: ‘‘} pwd = request.query_params.get(‘pwd‘) name = request.query_params.get(‘name‘) user = models.Userinfo.objects.filter(name=name, pwd=pwd).first() if not user: dic[‘msg‘] = ‘用户名或密码错误‘ dic[‘code‘] = 1001 return Response(dic) t = time.time() key = ‘%s|%s‘ % (name, t) m = hashlib.md5() m.update(key.encode(‘utf-8‘)) token = m.hexdigest() user.token = token user.save() dic[‘token‘] = token dic[‘msg‘] = ‘登陆成功‘ return Response(dic) class HostView(APIView): authentication_classes=[MyAuthentication,] def dispatch(self, request, *args, **kwargs): """ 请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能 """ return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): return Response(‘GET请求,响应内容‘) def post(self, request, *args, **kwargs): return Response(‘POST请求,响应内容‘) def put(self, request, *args, **kwargs): return Response(‘PUT请求,响应内容‘)
全局认证;在settings中配置
from django.db import models # Create your models here. class Userinfo(models.Model): name=models.CharField(max_length=32,verbose_name=‘用户名‘) pwd=models.CharField(max_length=32,verbose_name=‘密码‘) token=models.CharField(max_length=64,null=True) def __str__(self): return self.name
urlpatterns = [ url(r‘^admin/‘, admin.site.urls), url(r‘^users/‘, views.HostView.as_view()), url(r‘^auth/‘, views.AuthView.as_view()), ]
from rest_framework.exceptions import APIException from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from app01 import models class MyAuthentication(BaseAuthentication): def authenticate(self, request): token = request.query_params.get(‘token‘) user=models.Userinfo.objects.filter(token=token).first() if user: return (user.name, ‘aaaaaa‘) raise APIException(‘认证失败‘)
REST_FRAMEWORK = { ‘UNAUTHENTICATED_USER‘: None, ‘UNAUTHENTICATED_TOKEN‘: None, "DEFAULT_AUTHENTICATION_CLASSES": [ "app01.utils.MyAuthentication", ], }