从请求到响应过程中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实现爬虫下载漫画示例
Feb 16 Python
Python中多线程及程序锁浅析
Jan 21 Python
使用Python绘制图表大全总结
Feb 11 Python
Python中模块pymysql查询结果后如何获取字段列表
Jun 05 Python
python取代netcat过程分析
Feb 10 Python
Python 调用 Windows API COM 新法
Aug 22 Python
win10系统Anaconda和Pycharm的Tensorflow2.0之CPU和GPU版本安装教程
Dec 03 Python
tensorflow -gpu安装方法(不用自己装cuda,cdnn)
Jan 20 Python
Python实现区域填充的示例代码
Feb 03 Python
python实战之90行代码写个猜数字游戏
Apr 22 Python
python cv2图像质量压缩的算法示例
Jun 04 Python
Django+Celery实现定时任务的示例
Jun 23 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
总结的一些PHP开发中的tips(必看篇)
2017/03/24 PHP
js word表格动态添加代码
2010/06/07 Javascript
jquery ajax return没有返回值的解决方法
2011/10/20 Javascript
node.js中的fs.mkdirSync方法使用说明
2014/12/17 Javascript
jquery实现简单实用的打分程序实例
2015/07/23 Javascript
Jquery中巧用Ajax的beforeSend方法
2016/01/20 Javascript
Bootstrap学习笔记之css组件(3)
2016/06/07 Javascript
使用Script元素发送JSONP请求的方法
2016/06/12 Javascript
微信小程序 网络请求(GET请求)详解
2016/11/16 Javascript
js 点击a标签 获取a的自定义属性方法
2016/11/21 Javascript
jquery实现转盘抽奖功能
2017/01/06 Javascript
javascript 秒表计时器实现代码
2017/03/09 Javascript
vue2.0获取鼠标位置的方法
2018/09/13 Javascript
js简单的分页器插件代码实例
2019/09/11 Javascript
layui 数据表格 根据值(1=业务,2=机构)显示中文名称示例
2019/10/26 Javascript
es6中Promise 对象基本功能与用法实例分析
2020/02/23 Javascript
js实现QQ邮箱邮件拖拽删除功能
2020/08/27 Javascript
基于jQuery拖拽事件的封装
2020/11/29 jQuery
[00:14]PWL:老朋友Mushi拍VLOG与中国玩家问好
2020/11/04 DOTA
Python中functools模块的常用函数解析
2016/06/30 Python
使用Python实现跳帧截取视频帧
2019/05/31 Python
python 实现多线程下载视频的代码
2019/11/15 Python
Python猴子补丁知识点总结
2020/01/05 Python
浅析Django 接收所有文件,前端展示文件(包括视频,文件,图片)ajax请求
2020/03/09 Python
解决pycharm安装第三方库失败的问题
2020/05/09 Python
Python如何在main中调用函数内的函数方式
2020/06/01 Python
python 爬虫网页登陆的简单实现
2020/11/30 Python
HTML5本地数据库基础操作详解
2016/04/26 HTML / CSS
微信小程序之html5 canvas绘图并保存到系统相册
2019/06/20 HTML / CSS
战友聚会邀请函
2014/01/18 职场文书
国际贸易毕业生自荐书
2014/06/22 职场文书
2014年语文教学工作总结
2014/12/17 职场文书
干部考核工作总结
2015/08/12 职场文书
信息技术研修心得体会
2016/01/08 职场文书
Go语言设计模式之结构型模式
2021/06/22 Golang
Java字符串逆序方法详情
2022/03/21 Java/Android