从请求到响应过程中django都做了哪些处理


Posted in Python onAugust 01, 2018

前言

最近面试的时候,被面试官问道一个问题,就是 request.user 里面的 user 是怎样得到的,这个问题当时没有回答上来,可以说是非常的尴尬,所以赶快查了一些资料,看了一些源码,特地来总结一下这个问题。

要想回答为什么可以直接通过 request.user 得到请求的用户,应该先来看看请求被处理以及如何返回响应的流程。今天先总结一下 django 从请求到响应都进行了哪些过程。

WSGI

当客户端发送一次请求后,最先处理请求的实际上是 web 服务器就是我们经常说的 nginx、Apache 这类的 web 服务器,而 WSGI 的作用就是把 web 服务器和 web 框架 (Django) 连接起来。WSGI 被分为了两个部分:服务端和应用端。为了处理一个 WSGI 的响应,服务端执行应用程序并向应用端提供一个回调函数,应用端处理请求并使用提供的回调将响应返回给服务端。

本质上来讲,我觉得 WSGI 就是 web 服务器和 django 应用之间的一个联系人。

数据流

当用户向你的应用发送一个请求的时候,一个 WSGI handler 将会被初始化,它会完成以下工作:

  1. 导入 settings.py 和 django 的异常类
  2. 使用 load_middleware 方法加载 settings.py 中 MIDDLEWARE_CLASSES 或者 MIDDLEWARES 元组中所用的 middleware classes.
  3. 创建四个列表 (_request_middleware,_view_middleware, _response_middleware, _exception_middleware),里面分别包含处理 request,view,response 和 exception 的方法。
  4. WSGI Handler 将实例化一个 django.http.HTTPRequest 对象的子类,django.core.handlers.wsgi.WSGIRequest.
  5. 循环遍历处理 request 的方法 (_request_middleware 列表),并按照顺序调用他们
  6. 解析请求的 url
  7. 循环遍历每个处理 view 的方法 (_view_middleware 列表)
  8. 如果找的到的话,就调用视图函数
  9. 处理任何异常的方法 (_exception_middleware 列表)
  10. 循环遍历每个处理响应的方法 (_response_middleware 列表),(从内向外,与请求中间件的顺序相反)
  11. 最后得到一个响应,并调用 web server 提供的回调函数

中间件

中间件被用在了 django 的许多关键功能中:例如,使用 CSRF 中间键来防止跨站请求伪造攻击。它们也被用来处理会话数据,身份认证和授权同样是由中间件来完成的。我们也可以自己编写中间件来调整或者(短路)通过应用程序的数据流。

django 的中间件至少含有以下四个方法中的一个:process_request, process_response, process_view, process_exception。这些方法会被 WSGI handler 收集并按照顺序调用。

process_request

我们可以先来看看 django.contrib.auth.middleware.AuthenticationMiddleware:

def get_user(request):
 if not hasattr(request, '_cached_user'):
  request._cached_user = auth.get_user(request)
 return request._cached_user

class AuthenticationMiddleware(MiddlewareMixin):
 def process_request(self, request):
  assert hasattr(request, 'session'), (
    "The Django authentication middleware requires session middleware "
    "to be installed. Edit your MIDDLEWARE%s setting to insert "
    "'django.contrib.sessions.middleware.SessionMiddleware' before "
    "'django.contrib.auth.middleware.AuthenticationMiddleware'."
  ) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
  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 时,WSGI handler 会继续加载 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

到这一步之后 WSGI handler 知道了调用哪一个视图函数,以及传递哪些参数。它会再一次调用中间件列表里面的方法,这次是_view_middleware 列表。所有 Django 中间件的 process_view 方法将会被这样声明:

process_view(request, view_function, view_args, view_kwargs)

和 process_request 一样,process_view 函数必须返回 None 或者 HTTPResponse 对象,使得 WSGI handler 继续处理视图或者'短路'处理流程并返回一个响应。在 CSRF middleware 中存在一个 process_view 的方法。作用是当 CSRF cookies 出现时,process_view 方法将会返回 None, 视图函数将会继续的执行。如果不是这样,请求将会被拒绝,处理流程将会被'短路',会生成一个错误的信息。

进入视图函数

一个视图函数需要满足三个条件:

  • 必须是可以调用的。这可以是基于函数的视图或者是 class-based 的视图(继承自 View 并且使用 as_view() 方法来使它成为可调用的。这些方法的调用依赖 HTTP verb(GET, POST, etc))
  • 必须接受一个 HTTPRequest 对象作为第一个位置参数。这个 HTTPRequest 对象是被所有 process_request 和 process_view 中间件方法处理的结果。
  • 必须返回一个 HTTPResponse 对象,或者抛出一个异常。就是用这个 response 对象来开启 WSGI handler 的 process_view 循环。

process_exception

如果视图函数抛出一个异常,Handler 将会循环遍历_exception_middleware 列表,这些方法按照相反的顺序执行,从 settings.py 里面列出来的最后一个中间件到第一个。如果一个异常被抛出,处理过程将会被短路,其他的 process_exception 将不会被执行。通常我们依赖 Djnago's BaseHandler 提供的异常处理程序,但是我们也可以使用自定义的异常处理中间件。

process_response

在这个阶段,我们得到了一个 HTTPResponse 对象,这个对象可能是 process_view 返回的,也可能是视图函数返回的。现在我们将循环访问响应中间件。这是中间件调整数据的最后的机会。执行的顺序是从内向外执行。

以 cache middleware 的 process_response 为例:它依赖于你的 app 里面的不同的状态(缓存是否打开或者关闭,是否在处理一个数据流),来决定是否缓存你的响应。

注意

django 1.10 和之前版本的区别:

在旧版本的 MIDDLEWARE_CLASSES 中,就算一个中间件”短路”了执行过程,所有的中间件都会调用它们的 process_response 方法。而在新的 MIDDLEWARES 版本中,只有这个中间件和在它之前执行的中间件才会调用 process_response 方法。

总结

以上就是 django 在处理一个请求的基本的过程,最后 django 的 WSGI Handler 会创建一个来自 HTTPResponse 的返回值,而且会调用回调函数把数据传递给 web server, 最后返回给用户。

以下是两个关键点:

我们现在知道了视图函数是如何和 url 解析器匹配以及什么在调用它 (WSGI Handler)

有四个关键的地方可以让你挂钩到请求 / 响应周期:process_request, process_response, process_view, process_exception。请求中间件是从外部向内执行,最后抵达到视图函数,然后通过响应中间件从内向外返回。

参考资料

  • Django Middlewares and the Request/Response Cycle
  • How Django processes a request

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python sys模块sys.path使用方法示例
Dec 04 Python
Python压缩和解压缩zip文件
Feb 14 Python
给Python中的MySQLdb模块添加超时功能的教程
May 05 Python
python实现验证码识别功能
Jun 07 Python
Python对excel文档的操作方法详解
Dec 10 Python
Python OpenCV之图片缩放的实现(cv2.resize)
Jun 28 Python
Python调用.NET库的方法步骤
Dec 27 Python
python面向对象之类属性和类方法案例分析
Dec 30 Python
python异常处理try except过程解析
Feb 03 Python
django实现更改数据库某个字段以及字段段内数据
Mar 31 Python
Selenium使用Chrome模拟手机浏览器方法解析
Apr 10 Python
Python获取百度热搜的完整代码
Apr 07 Python
Python WSGI的深入理解
Aug 01 #Python
Django进阶之CSRF的解决
Aug 01 #Python
python3利用venv配置虚拟环境及过程中的小问题小结
Aug 01 #Python
mvc框架打造笔记之wsgi协议的优缺点以及接口实现
Aug 01 #Python
python爬虫自动创建文件夹的功能
Aug 01 #Python
浅谈关于Python3中venv虚拟环境
Aug 01 #Python
python Web开发你要理解的WSGI & uwsgi详解
Aug 01 #Python
You might like
Phpbean路由转发的php代码
2008/01/10 PHP
PHP通过串口实现发送短信
2015/07/08 PHP
利用PHPStorm如何开发Laravel应用详解
2017/08/30 PHP
客户端js判断文件类型和文件大小即限制上传大小
2013/11/20 Javascript
jQuery回调函数的定义及用法实例
2014/12/23 Javascript
jQuery菜单插件superfish使用指南
2015/04/21 Javascript
JS实现网页上随机产生超链接地址的方法
2015/11/09 Javascript
利用jquery制作滚动到指定位置触发动画
2016/03/26 Javascript
jQuery的 $.ajax防止重复提交的两种方法(推荐)
2016/10/14 Javascript
H5移动端图片压缩上传开发流程
2016/11/09 Javascript
javascript实现滑动解锁功能
2017/03/22 Javascript
使用vue-infinite-scroll实现无限滚动效果
2018/06/22 Javascript
React 源码中的依赖注入方法
2018/11/07 Javascript
浅谈layui分页控件field参数接收对象的问题
2019/09/20 Javascript
webpack3.0升级4.0的方法步骤
2020/04/02 Javascript
Javascript实现打鼓效果
2021/01/29 Javascript
[01:54]TI珍贵瞬间系列(三):翻盘
2020/08/28 DOTA
以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
2016/01/20 Python
利用 Monkey 命令操作屏幕快速滑动
2016/12/07 Python
python机器学习之KNN分类算法
2018/08/29 Python
Python语言进阶知识点总结
2019/05/28 Python
Django网络框架之创建虚拟开发环境操作示例
2019/06/06 Python
Python自动化运维之Ansible定义主机与组规则操作详解
2019/06/13 Python
Python pandas DataFrame操作的实现代码
2019/06/21 Python
Django模板Templates使用方法详解
2019/07/19 Python
Python lambda表达式filter、map、reduce函数用法解析
2019/09/11 Python
简单瞅瞅Python vars()内置函数的实现
2019/09/27 Python
浅谈python累加求和+奇偶数求和_break_continue
2020/02/25 Python
python中remove函数的踩坑记录
2021/01/04 Python
详解Python中的Lock和Rlock
2021/01/26 Python
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
2014/09/09 面试题
工程造价专业大专生求职信
2013/10/06 职场文书
小型女装店的创业计划书
2014/01/09 职场文书
中学生自我评价范文
2015/03/03 职场文书
入党自荐书范文
2015/03/05 职场文书
2015年为民办实事工作总结
2015/05/26 职场文书