从请求到响应过程中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获取Linux系统的各种信息
Jul 10 Python
举例讲解Python设计模式编程中对抽象工厂模式的运用
Mar 02 Python
利用matplotlib+numpy绘制多种绘图的方法实例
May 03 Python
Python实现一个转存纯真IP数据库的脚本分享
May 21 Python
Pycharm在创建py文件时,自动添加文件头注释的实例
May 07 Python
python 把列表转化为字符串的方法
Oct 23 Python
python元组和字典的内建函数实例详解
Oct 22 Python
Django中使用MySQL5.5的教程
Dec 18 Python
tensorflow中tf.slice和tf.gather切片函数的使用
Jan 19 Python
Python list运算操作代码实例解析
Jan 20 Python
python实现PDF中表格转化为Excel的方法
Jun 16 Python
Jupyter Notebook添加代码自动补全功能的实现
Jan 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
第七节--类的静态成员
2006/11/16 PHP
PHP基于工厂模式实现的计算器实例
2015/07/16 PHP
详解php中serialize()和unserialize()函数
2017/07/08 PHP
laravel框架关于搜索功能的实现
2018/03/15 PHP
laravel 框架配置404等异常页面
2019/01/07 PHP
Jquery中获取iframe的代码
2011/01/11 Javascript
仿jQuery的siblings效果的js代码
2011/08/09 Javascript
JS判断表单输入是否为空(示例代码)
2013/12/23 Javascript
node.js中的url.resolve方法使用说明
2014/12/10 Javascript
使用AngularJS制作一个简单的RSS阅读器的教程
2015/06/18 Javascript
用jQuery向div中添加Html文本内容的简单实现
2016/07/13 Javascript
jQuery实现表格隔行及滑动,点击时变色的方法【测试可用】
2016/08/20 Javascript
vue配置请求本地json数据的方法
2018/04/11 Javascript
vue-cli项目中使用echarts图表实例
2018/10/22 Javascript
layDate日期控件使用方法详解
2018/11/15 Javascript
微信小程序的tab选项卡的实现效果
2019/05/15 Javascript
如何使用proxy实现一个简单完整的MVVM库的示例代码
2019/09/17 Javascript
angular8和ngrx8结合使用的步骤介绍
2019/12/01 Javascript
js实现随机抽奖
2020/03/19 Javascript
python循环监控远程端口的方法
2015/03/14 Python
关于Python中Inf与Nan的判断问题详解
2017/02/08 Python
一个简单的python爬虫程序 爬取豆瓣热度Top100以内的电影信息
2018/04/17 Python
Python3.6.x中内置函数总结及讲解
2019/02/22 Python
Python之关于类变量的两种赋值区别详解
2020/03/12 Python
Tensorflow之MNIST CNN实现并保存、加载模型
2020/06/17 Python
python安装mysql的依赖包mysql-python操作
2021/01/01 Python
金牌葡萄酒俱乐部:Gold Medal Wine Club
2017/11/02 全球购物
VLAN和VPN有什么区别?分别实现在OSI的第几层?
2014/12/23 面试题
销售辞职报告范文
2014/01/12 职场文书
环境建设实施方案
2014/03/14 职场文书
副总经理岗位职责
2014/03/16 职场文书
怎样拟定创业计划书
2014/05/01 职场文书
物理分数没达标检讨书
2014/09/13 职场文书
2015年网管个人工作总结
2015/05/22 职场文书
小学班主任心得体会
2016/01/07 职场文书
Java输出Hello World完美过程解析
2021/06/13 Java/Android