源码剖析Django REST framework的请求生命周期

学习Django的时候知道,在Django请求的生命周期中,请求经过WSGI和中间件到达路由,不管是FBV还是CBV都会先执行View视图函数中的dispatch方法

REST framework是基于Django的API框架,REST framework采用的是CBV的请求模式.

所以在一个项目中,使用了REST framework的时候,

请求到达REST framework后,也先执行REST framework中的dispatch方法

先来看看dispatch方法的源码

def dispatch(self, request, *args, **kwargs):

    self.args = args            # 函数传递过来的参数
    self.kwargs = kwargs        # 函数传递过来的参数

    # 封装request
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        self.initial(request, *args, **kwargs)

        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

查看initialize_request方法,可以知道这个方法接收客户端的request请求,再重新封装成新的request

def initialize_request(self, request, *args, **kwargs):

    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
    )

再查看Request方法的源码

可以知道这个Request类是rest framework中定义的一个类

class Request(object):

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

        if self.parser_context is None:
            self.parser_context = {}
        self.parser_context[‘request‘] = self
        self.parser_context[‘encoding‘] = request.encoding or settings.DEFAULT_CHARSET

        force_user = getattr(request, ‘_force_auth_user‘, None)
        force_token = getattr(request, ‘_force_auth_token‘, None)
        if force_user is not None or force_token is not None:
            forced_auth = ForcedAuthentication(force_user, force_token)
            self.authenticators = (forced_auth,)

先不看这个Request到底执行了什么操作

但是已经知道经过Request处理过的request已经不再是客户端发送过来的那个request了

initialize_request方法中,有一个方法处理过request,来看看get_parser_context方法的源码

def get_parser_context(self, http_request):

    return {
        ‘view‘: self,
        ‘args‘: getattr(self, ‘args‘, ()),
        ‘kwargs‘: getattr(self, ‘kwargs‘, {})
    }

在这里,view的值是self,代指的是UsersView这个对象,所以get_parser_context方法把UsersView这个类封装进来然后返回

所以get_parser_context方法最后返回的当前对象以及当前对象所传的参数

经过initialize_request函数处理之后的request,现在就变成了

Request(
    request,
    parsers=self.get_parsers(),
    authenticators=self.get_authenticators(),
    negotiator=self.get_content_negotiator(),
    parser_context=parser_context
)

现在再来看看Request的其他参数代指的是什么

get_parsers                 根据字面意思,是解析get请求的意思
get_authenticators          认证相关
get_content_negotiator      选择相关
parser_context              封闭self和self的参数

def get_parsers(self):

    return [parser() for parser in self.parser_classes]

def get_authenticators(self):

    return [auth() for auth in self.authentication_classes]

def get_permissions(self):

    return [permission() for permission in self.permission_classes]

def get_throttles(self):

    return [throttle() for throttle in self.throttle_classes]

def get_content_negotiator(self):

    if not getattr(self, ‘_negotiator‘, None):
        self._negotiator = self.content_negotiation_class()
    return self._negotiator

再来看看UsersView这个类中的get方法和post方法

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

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

可以看到get方法的参数中有一个request,通过前面可以知道这个request已经不是最开始时到达服务端的request了

这个request方法中已经被REST framework封装了解析,认证和选择等相关的方法

def dispatch(self, request, *args, **kwargs):

    self.args = args
    self.kwargs = kwargs
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        self.initial(request, *args, **kwargs)

        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

default_response_headers这个方法从它的注释可以看出已经被丢弃了.

再来看initial这个方法

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)

先执行get_format_suffix来获取客户端所发送的url的后缀

然后执行perform_content_negotiation方法,从它的注释可以知道这个方法的主要作用是执行内容选择,并把服务端接收到的信息保存在request中

然后再执行determine_version方法

def determine_version(self, request, *args, **kwargs):
    """
    If versioning is being used, then determine any API version for the
    incoming request. Returns a two-tuple of (version, versioning_scheme)
    """
    if self.versioning_class is None:
        return (None, None)
    scheme = self.versioning_class()
    return (scheme.determine_version(request, *args, **kwargs), scheme)

determine_version方法的官方注释中可以知道,determine_version方法的主要作用是

如果url中有版本信息,就获取发送到服务端的版本,返回一个元组

执行完上面的方法,再执行perform_authentication方法来进行认证操作

来看下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

从上面有代码及注释中可以看出,perform_authentication方法的作用就是

执行认证功能,确认进行后续操作的用户是被允许的.
perform_authentication方法返回经过认证的用户对象

执行完perform_authentication方法,就会执行check_permissions方法

def check_permissions(self, request):
    """
    Check if the request should be permitted.
    Raises an appropriate exception if the request is not permitted.
    """
    for permission in self.get_permissions():
        if not permission.has_permission(request, self):
            self.permission_denied(
                request, message=getattr(permission, ‘message‘, None)
            )
            

check_permissions方法的作用是

如果用户通过认证,检查用户是否有权限访问url中所传的路径.
如用用户访问的是没有没有权限的路径,则会抛出异常.

check_permissions方法执行完成后,就会执行check_throttles方法

check_throttles方法的作用是检查用户是否被限制了访问主机的次数

如果用户访问服务器的次数超出设定值,则会抛出一个异常

例如,如果想限制一个ip地址每秒钟只能访问几次,一个小时之内最多可以访问多少次,就可以在settings.py文件中进行配置

def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())

initial这个方法执行完成后,request.method.lower把请求的方法转换成小写

# 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)

再通过通过反射的方式来执行UsersView类中的get或post等自定义方法

需要注意的是,在执行initial方法之前,使用了try/except方法来进行异常处理

如果执行initial方法的时候出现错误,就调用handle_exception来处理initial方法抛出的异常,返回正确的响应信息

def handle_exception(self, exc):
    """
    Handle any exception that occurs, by returning an appropriate response,
    or re-raising the error.
    """
    if isinstance(exc, (exceptions.NotAuthenticated,
                        exceptions.AuthenticationFailed)):
        # WWW-Authenticate header for 401 responses, else coerce to 403
        auth_header = self.get_authenticate_header(self.request)

        if auth_header:
            exc.auth_header = auth_header
        else:
            exc.status_code = status.HTTP_403_FORBIDDEN

    exception_handler = self.get_exception_handler()

    context = self.get_exception_handler_context()
    response = exception_handler(exc, context)

    if response is None:
        self.raise_uncaught_exception(exc)

    response.exception = True
    return response

在前面,如果initial方法执行完成没有抛出异常,则根据反射执行自定义的请求方法,然后返回响应信息

如果initial方法抛出异常则执行handle_exception方法处理抛出的异常,也返回响应信息

等到上面的过程执行完成后,再执行finalize_response方法把最终的响应信息返回给客户端的浏览器

def finalize_response(self, request, response, *args, **kwargs):
    """
    Returns the final response object.
    """
    # Make the error obvious if a proper response is not returned
    assert isinstance(response, HttpResponseBase), (
        ‘Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` ‘
        ‘to be returned from the view, but received a `%s`‘
        % type(response)
    )

    if isinstance(response, Response):
        if not getattr(request, ‘accepted_renderer‘, None):
            neg = self.perform_content_negotiation(request, force=True)
            request.accepted_renderer, request.accepted_media_type = neg

        response.accepted_renderer = request.accepted_renderer
        response.accepted_media_type = request.accepted_media_type
        response.renderer_context = self.get_renderer_context()

    # Add new vary headers to the response instead of overwriting.
    vary_headers = self.headers.pop(‘Vary‘, None)
    if vary_headers is not None:
        patch_vary_headers(response, cc_delim_re.split(vary_headers))

    for key, value in self.headers.items():
        response[key] = value

    return response

所以总结:

REST framework请求的生命周期为:

1.请求到达服务端,经过WSGI和中间件到达路由系统
2.路由系统执行配置的CBV或者FBV中的dispatch方法
3.在dispatch方法中,request方法被封装添加了解析器,认证方法及选择器等方法
4.然后执行initial方法
5.再获取版本,进行认证操作,权限操作和节流操作
6.最后执行自定义的get,post,push,delete等自定义方法
7.在执行initial方法之前,通过try来捕获可能出现的异常
8.如果出现异常,就执行handle_exception方法来处理捕获到的异常
9.不管是否出现异常,最后的返回值都通过finalize_response方法来处理响应的内容
时间: 2024-07-28 23:50:14

源码剖析Django REST framework的请求生命周期的相关文章

源码剖析Django REST framework的认证方式及自定义认证

源码剖析Django REST framework的认证方式 由Django的CBV模式流程,可以知道在url匹配完成后,会执行自定义的类中的as_view方法. 如果自定义的类中没有定义as_view方法,根据面向对象中类的继承可以知道,则会执行其父类View中的as_view方法 在Django的View的as_view方法中,又会调用dispatch方法. 现在来看看Django restframework的认证流程 Django restframework是基于Django的框架,所以基

Spark源码系列(四)图解作业生命周期

这一章我们探索了Spark作业的运行过程,但是没把整个过程描绘出来,好,跟着我走吧,let you know! 我们先回顾一下这个图,Driver Program是我们写的那个程序,它的核心是SparkContext,回想一下,从api的使用角度,RDD都必须通过它来获得. 下面讲一讲它所不为认知的一面,它和其它组件是如何交互的. Driver向Master注册Application过程 SparkContext实例化之后,在内部实例化两个很重要的类,DAGScheduler和TaskSched

SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过service()方法,委派到doGet()或者doPost()这些方法,完成Http请求的处理. 在初始化流程中,SpringMVC巧妙的运用依赖注入读取参数,并最终建立一个与容器上下文相关联的spring子上下文.这个子上下文,就像Struts2中xwork容器一样,为接下来的Http处理流程中各种编程

[转载]ASP.NET Core 源码阅读笔记(2) ---Microsoft.Extensions.DependencyInjection生命周期管理

在上一篇文章中我们主要分析了ASP.NET Core默认依赖注入容器的存储和解析,这一篇文章主要补充一下上一篇文章忽略的一些细节:有关服务回收的问题,即服务的生命周期问题.有关源码可以去GitHub上找到. 这次的主角就是ServiceProvider一人,所有有关生命周期的源码几乎都集中在ServiceProvider.cs这个文件中. 我们知道服务的生命周期由三种,分别是: Transient Scoped Singleton 首先给出我的结论:这三种生命周期类别本质上没有区别,服务的生命周

Tomcat 源码分析(一)——启动与生命周期组件

写在前面的话:读Tomcat源码也有段时间了,大领悟谈不上.一些小心得记录下来,供大家参考相护学习. 一.启动流程 Tomcat启动首先需要熟悉的是它的启动流程.和初学者第一天开始写Hello World一样,Tomcat的启动也依赖main方法. 1 /* 2 * org.apache.catalina.startup.Bootstrap 3 */ 4 if (daemon == null) { 5 Bootstrap bootstrap = new Bootstrap(); // 实例对象

Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax csrf相关装饰器 在CBV上加csrf装饰器 Django settings源码剖析及模仿使用 Django settings源码剖析 查看内部配置文件 模仿使用 Auth模块 auth简介 auth模块常用方法 创建用户 校验用户名和密码 保存用户登录状态 判断当前用户是否登录 校验原密码 修改密

Django Rest Framework源码剖析(二)-----权限

一.简介 在上一篇博客中已经介绍了django rest framework 对于认证的源码流程,以及实现过程,当用户经过认证之后下一步就是涉及到权限的问题.比如订单的业务只能VIP才能查看,所以这时候需要对权限进行控制.下面将介绍DRF的权限控制源码剖析. 二.基本使用 这里继续使用之前的示例,加入相应的权限,这里先介绍使用示例,然后在分析权限源码 1.在django 项目下新建立目录utils,并建立permissions.py,添加权限控制: class MyPremission(obje

Django Rest Framework源码剖析(三)-----频率控制

一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过代理限制访问频率等,但是django rest framework自身就提供了访问频率的控制,可以从代码本身做控制. 二.频率控制内部原理概述 django rest framework 中频率控制基本原理基于访问次数和时间,通过计算实现,当然我们也可以自己定义频率控制方法.基本原理如下: 启用频率

Django Rest Framework源码剖析(八)-----视图与路由

一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使用者而言不同的视图具有不同的功能,这样我们可以根据需求定制自己视图.以下是官网传送门:http://www.django-rest-framework.org/api-guide/views/ 在之前的文章中,由于参杂了权限.认证等(如果不了解请看博客的以前的文章),但在本章中基本可以不使用,所进使