从请求到响应过程中django都做了哪些处理
前言
最近面试的时候,被面试官问道一个问题,就是request.user里面的user是怎样得到的,这个问题当时没有回答上来,可以说是非常的尴尬,所以赶快查了一些资料,看了一些源码,特地来总结一下这个问题。
要想回答为什么可以直接通过request.user得到请求的用户,应该先来看看请求被处理以及如何返回响应的流程。今天先总结一下django从请求到响应都进行了哪些过程。
WSGI
当客户端发送一次请求后,最先处理请求的实际上是web服务器就是我们经常说的nginx、Apache这类的web服务器,而WSGI的作用就是把web服务器和web框架(Django)连接起来。WSGI被分为了两个部分:服务端和应用端。为了处理一个WSGI的响应,服务端执行应用程序并向应用端提供一个回调函数,应用端处理请求并使用提供的回调将响应返回给服务端。
本质上来讲,我觉得WSGI就是web服务器和django应用之间的一个联系人。
数据流
当用户向你的应用发送一个请求的时候,一个WSGIhandler将会被初始化,它会完成以下工作:
- 导入settings.py和django的异常类
- 使用load_middleware方法加载settings.py中MIDDLEWARE_CLASSES或者MIDDLEWARES元组中所用的middlewareclasses.
- 创建四个列表(_request_middleware,_view_middleware,_response_middleware,_exception_middleware),里面分别包含处理request,view,response和exception的方法。
- WSGIHandler将实例化一个django.http.HTTPRequest对象的子类,django.core.handlers.wsgi.WSGIRequest.
- 循环遍历处理request的方法(_request_middleware列表),并按照顺序调用他们
- 解析请求的url
- 循环遍历每个处理view的方法(_view_middleware列表)
- 如果找的到的话,就调用视图函数
- 处理任何异常的方法(_exception_middleware列表)
- 循环遍历每个处理响应的方法(_response_middleware列表),(从内向外,与请求中间件的顺序相反)
- 最后得到一个响应,并调用webserver提供的回调函数
中间件
中间件被用在了django的许多关键功能中:例如,使用CSRF中间键来防止跨站请求伪造攻击。它们也被用来处理会话数据,身份认证和授权同样是由中间件来完成的。我们也可以自己编写中间件来调整或者(短路)通过应用程序的数据流。
django的中间件至少含有以下四个方法中的一个:process_request,process_response,process_view,process_exception。这些方法会被WSGIhandler收集并按照顺序调用。
process_request
我们可以先来看看django.contrib.auth.middleware.AuthenticationMiddleware:
defget_user(request): ifnothasattr(request,'_cached_user'): request._cached_user=auth.get_user(request) returnrequest._cached_user classAuthenticationMiddleware(MiddlewareMixin): defprocess_request(self,request): asserthasattr(request,'session'),( "TheDjangoauthenticationmiddlewarerequiressessionmiddleware" "tobeinstalled.EdityourMIDDLEWARE%ssettingtoinsert" "'django.contrib.sessions.middleware.SessionMiddleware'before" "'django.contrib.auth.middleware.AuthenticationMiddleware'." )%("_CLASSES"ifsettings.MIDDLEWAREisNoneelse"") request.user=SimpleLazyObject(lambda:get_user(request))
这里我们可以发现request.user这个属性是在AuthenticationMiddleware中产生的。这个我们稍后再说。
这里我们可以发现,这个中间件只有process_request,说明它只在request这一步处理流入和流出django应用的数据流。这个中间件会首先验证会话中间件是否被使用,然后通过调用get_user函数来设置用户。当WSGI处理程序迭代
process_request方法列表的时候,它将会构建这个最终会被传递给视图函数的请求对象,并能够使你引用request.user。一些中间件没有process_request方法,在这个阶段,会被跳过。
process_request应该返回None或者HTTPResponse对象。当返回None时,WSGIhandler会继续加载process_request里面的方法,但是后一种情况会短路处理过程并进入process_response循环。
解析url
当所有的process_request被调用完之后,我们就会得到一个将被传递给视图函数的request对象。当这个事件发生之前,django必须解析url并决定调用哪一个视图函数。这个过程非常简单,只需要使用正则匹配即可。settings.py中有一个ROOT_URLCONF键来指定根url.py,在这里会包含你所有app的urls.py文件。如果没有匹配成功,将会抛出一个异常django.core.urlresolvers.Resolver404,这是django.http.HTTP404的子类。
process_view
到这一步之后WSGIhandler知道了调用哪一个视图函数,以及传递哪些参数。它会再一次调用中间件列表里面的方法,这次是_view_middleware列表。所有Django中间件的process_view方法将会被这样声明:
process_view(request,view_function,view_args,view_kwargs)
和process_request一样,process_view函数必须返回None或者HTTPResponse对象,使得WSGIhandler继续处理视图或者'短路'处理流程并返回一个响应。在CSRFmiddleware中存在一个process_view的方法。作用是当CSRFcookies出现时,process_view方法将会返回None,视图函数将会继续的执行。如果不是这样,请求将会被拒绝,处理流程将会被'短路',会生成一个错误的信息。
进入视图函数
一个视图函数需要满足三个条件:
- 必须是可以调用的。这可以是基于函数的视图或者是class-based的视图(继承自View并且使用as_view()方法来使它成为可调用的。这些方法的调用依赖HTTPverb(GET,POST,etc))
- 必须接受一个HTTPRequest对象作为第一个位置参数。这个HTTPRequest对象是被所有process_request和process_view中间件方法处理的结果。
- 必须返回一个HTTPResponse对象,或者抛出一个异常。就是用这个response对象来开启WSGIhandler的process_view循环。
process_exception
如果视图函数抛出一个异常,Handler将会循环遍历_exception_middleware列表,这些方法按照相反的顺序执行,从settings.py里面列出来的最后一个中间件到第一个。如果一个异常被抛出,处理过程将会被短路,其他的process_exception将不会被执行。通常我们依赖Djnago'sBaseHandler提供的异常处理程序,但是我们也可以使用自定义的异常处理中间件。
process_response
在这个阶段,我们得到了一个HTTPResponse对象,这个对象可能是process_view返回的,也可能是视图函数返回的。现在我们将循环访问响应中间件。这是中间件调整数据的最后的机会。执行的顺序是从内向外执行。
以cachemiddleware的process_response为例:它依赖于你的app里面的不同的状态(缓存是否打开或者关闭,是否在处理一个数据流),来决定是否缓存你的响应。
注意
django1.10和之前版本的区别:
在旧版本的MIDDLEWARE_CLASSES中,就算一个中间件”短路”了执行过程,所有的中间件都会调用它们的process_response方法。而在新的MIDDLEWARES版本中,只有这个中间件和在它之前执行的中间件才会调用process_response方法。
总结
以上就是django在处理一个请求的基本的过程,最后django的WSGIHandler会创建一个来自HTTPResponse的返回值,而且会调用回调函数把数据传递给webserver,最后返回给用户。
以下是两个关键点:
我们现在知道了视图函数是如何和url解析器匹配以及什么在调用它(WSGIHandler)
有四个关键的地方可以让你挂钩到请求/响应周期:process_request,process_response,process_view,process_exception。请求中间件是从外部向内执行,最后抵达到视图函数,然后通过响应中间件从内向外返回。
参考资料
- DjangoMiddlewaresandtheRequest/ResponseCycle
- HowDjangoprocessesarequest
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。