django之中间件(middleware)
django之中间件(middleware)
在之前一篇博文中,有关django的请求流程中,我们关于中间件这一层并没有详细的介绍,在这张图中,我们将中间层定义为django网关层和路由层的过渡层,那么具体会中间件会做什么事呢。
django默认中间件
在配置文件中,有一个MIDDLEWARE列表,里面存放了七个字符串,实际上这就是七个中间件程序,其基础作用就是处理我们的请求和响应,比如第四个中间件就经常妨碍我们发送简单的POST请求,所以初学阶段经常需要将其注释掉不让其生效:
这七个中间件程序会在请求来时自上而下依次的处理request请求,在响应走时自下而上依次处理response响应。
django中间件原理
中间件程序结构
这七个字符串实际上对应的是中间件程序存储的路径,我们可以顺着路径进去看一下它们的结构:
这七个程序都遵循着这样的结构:
-
继承了MiddlewareMixin类
from django.utils.deprecation import MiddlewareMixin -
可能做了一些初始化准备
__init__
-
拥有process_request和process_response函数
process_request需要传入request参数,没返回值
process_response需要传入request和response两个参数,必须返回response
自定义中间件
在知道以上结构后我们可以自己自定义中间件程序:
-
首先,我们需要一个py文件存放程序,这个位置最好相对固定
-
其次,编写类,导入MiddlewareMixin继承,写好基础的类体函数:
process_request
|process_response
注意这几个函数具有的特征。from django.utils.deprecation import MiddlewareMixin class MidWare01(MiddlewareMixin): def process_request(self, request): pass def process_response(self, request, response): return response -
最后,将编写好的类的路径及类名按格式注册到MIDDLEWARE的列表中。
中间件程序执行顺序
关于process_request和process_response是中间件最常用的函数,我们重点研究这个函数的顺序即可。在自定义中间件中加一点小程序测试一下:
当我们从网页向网址发送请求(无论有没有对应路由),后端会打印:
我们可以看出一些结论:
- 请求会经过中间件程序,按注册顺序自上而下的运行每个中间件process_request程序
- 响应会经过中间件程序,按注册顺序自下而上的运行每个中间件的process_response程序。
我们还可以做进一步的测试,如将process_request设置一个httpResponse返回值和将process_response的response替换成其他响应。
直接给出结论:
- 当中间件的process_request函数返回httpResponse对象,那么就之间去执行当前中间件的process_response函数,这个返回值传入response参数
- 中间件的process_response的返回值是传递性的,中途可以返回其他的response,返回什么,下一轮的response就接收什么,最终传递给用户浏览器。
我们可以用图解来解释这几个结论:
ps:中间件请求函数返回httpResponse对象,在django中是直接执行当前层中间件的process_response,而在flask中,是从最底层的中间件程序的响应函数开始执行的
字符串列表注册原理
如何通过字符串列表来实现中间件程序的注册的,我们需要两个知识点的前置学习:
importlib模块
importlib可以帮助我们用字符串导入模块,拿到一个模块名的字符串,它可以搜索模块,并执行导入操作:
import importlib # 导入importlib模块 s1 = 'aaa' res = importlib.import_module(s1) # 相当于执行import aaa 并将模块名设置为res res.aaa_attr # 可以通过res点出aaa模块的属性 s2 = 'pack.md' ret = importlib.import_module(s2) # 相当于执行from pack import md 并将模块名设置为ret ret.md_func() # 可以通过ret点出md模块的属性方法
import_module方法可以对句点隔开的字符串进行识别,但注意,最终导入的最小单位是py文件,不能导入py文件中的变量
模块对象的反射
在python中一切皆对象,模块也是一种对象,模块的名称空间中的名字就是模块对象的属性。
所以对于一个模块,我们可以通过反射的方式取到其中的类、函数、变量等名字:
import aaa attr = getattr(aaa, 'func') # 寻找aaa模块中的func属性 attr() # 直接加扩号进行执行
仿django配置文件注册
知道以上两个知识点,我们也可以让仿中间件的注册过程,写好类后,用字符串保存路径,然后顺序执行。核心代码就是将字符串识别成可以执行的类程序:
# MyMiddleWare文件夹midware01.py中 class MidWare01(MiddlewareMixin): def process_request(self, request): print('from MidWare01 request') # MyMiddleWare文件夹midware02.py中 class MidWare02(MiddlewareMixin): def process_request(self, request): print('from MidWare01 request') # 注册配置 MIDWARE_LIST = [ 'MyMiddleWare.midware01.MidWare01', 'MyMiddleWare.midware02.MidWare02', ] # 按照注册顺序,执行每个类中的process_request方法 import importlib def all_exec(request): for full_path in MIDWARE_LIST: # 将字符串切割为模块路径和类名 path, cls_name = full_path.rsplit('.', max_split=1) # 通过模块路径字符串拿到模块名 module_name = importlib.import_module(path) # 通过模块名反射拿到类名 cls = getattr(module_name, cls_name) # 通过类产生对象 obj = cls() # 通过对象使用方法 obj.process_request(request) # 多态的思想,因为大家的这个功能一致,所以方法名也该一致
自定义中间件
在上一小节已经介绍过自定义中间件的大致方法,这里简单罗列一下:
- 首先,我们需要一个py文件存放程序,这个位置最好相对固定
- 其次,编写中间件的类,导入MiddlewareMixin继承,写好基础的类体函数
- 最后,将编写好的类的路径及类名按格式注册到MIDDLEWARE的列表中。
其中第二步的类体函数,其实对应有很多的中间件方法,这里将会介绍5个中间件方法。
两个常见的自定义方法
process_request
- 这个方法是用于处理请求的,在网关处理好request之后,路由层处理路由之前,依注册顺序执行。
- 需要定义request形参接收请求,对请求做一系列判断。
- 如果process_request返回HttpResponse对象,则不会再执行后续的中间件以及路由等程序,直接将返回值传入process_response。
process_response
- 这个方法是用于处理响应的,在视图层返回HttpResponse对象的响应后,网关层处理响应之前,由各中间件依注册顺序自下而上执行。
- 需要定义request、response形参接收请求和响应,对响应做一定的处理。
- 返回值一定要将response传递走或者返回其他的HttpResponse对象。
三个了解的自定义方法
- process_view
路由匹配成功之后执行视图函数/类之前自动触发(顺序同process_request) - process_exception
视图函数/类执行报错自动触发(顺序同process_response) - process_template_response
视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)
这三个方法的使用频率十分的低,不过我们需要注意到中间件的职能并不局限于网关和django程序之间了,还有可能是路由与视图层之间,处理错误的时候,视图层与模板层之间。
通过这些我们信息我们也可以绘制一张新的django请求生命流程图。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!