Django中间件
一、中间件的概念
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
Django的中间件的定义:
1 | Middleware is a framework of hooks into Django’s request / response processing. <br>It’s a light, low - level “plugin” system for globally altering Django’s input or output. |
如果想修改请求,例如被传送到view中的HttpRequest对象。 或者想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。
可能还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。
Django默认的Middleware可以在settings.py中查看
:
1 2 3 4 5 6 7 8 9 | MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware' , 'django.contrib.sessions.middleware.SessionMiddleware' , 'django.middleware.common.CommonMiddleware' , 'django.middleware.csrf.CsrfViewMiddleware' , 'django.contrib.auth.middleware.AuthenticationMiddleware' , 'django.contrib.messages.middleware.MessageMiddleware' , 'django.middleware.clickjacking.XFrameOptionsMiddleware' , ] |
MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。
我们之前已经接触过一个csrf相关的中间件了,之前将这一行注释,再提交post请求的时候,就不会被forbidden了,后来学会使用csrf_token就不用再注释这个中间件了。
手动引入一个中间件:
1 | from django.middleware.security import SecurityMiddleware |
查看中间件源码:

import re from django.conf import settings from django.http import HttpResponsePermanentRedirect from django.utils.deprecation import MiddlewareMixin class SecurityMiddleware(MiddlewareMixin): def __init__(self, get_response=None): self.sts_seconds = settings.SECURE_HSTS_SECONDS self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS self.sts_preload = settings.SECURE_HSTS_PRELOAD self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER self.redirect = settings.SECURE_SSL_REDIRECT self.redirect_host = settings.SECURE_SSL_HOST self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT] self.get_response = get_response def process_request(self, request): path = request.path.lstrip("/") if (self.redirect and not request.is_secure() and not any(pattern.search(path) for pattern in self.redirect_exempt)): host = self.redirect_host or request.get_host() return HttpResponsePermanentRedirect( "https://%s%s" % (host, request.get_full_path()) ) def process_response(self, request, response): if (self.sts_seconds and request.is_secure() and 'strict-transport-security' not in response): sts_header = "max-age=%s" % self.sts_seconds if self.sts_include_subdomains: sts_header = sts_header + "; includeSubDomains" if self.sts_preload: sts_header = sts_header + "; preload" response["strict-transport-security"] = sts_header if self.content_type_nosniff and 'x-content-type-options' not in response: response["x-content-type-options"] = "nosniff" if self.xss_filter and 'x-xss-protection' not in response: response["x-xss-protection"] = "1; mode=block" return response
每个中间件都有具体的功能。
二、自定义中间件
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
1 2 3 4 5 6 7 8 9 | process_request( self ,request) process_view( self , request, view_func, view_args, view_kwargs) process_template_response( self ,request,response) process_exception( self , request, exception) process_response( self , request, response) |
以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。
1、process_request, process_response
当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求是process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。
上述截图中的中间件都是django中的,我们也可以自己定义一个中间件,自己写一个类,但是必须继承MiddlewareMixin。
自定义中间件需要引入:
1 | from django.utils.deprecation import MiddlewareMixin |
在视图函数中:
1 2 3 | def index(request): print ( "index....." ) return HttpResponse( "Index" ) |
在自定义中间件my_middlewares.py中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class CustomerMiddleware(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware process_request..." ) def process_response( self , request, response): # 必须有返回值,要一层层往回传,不加就会报错 print ( "CustomMiddleware process_response" ) return response class CustomerMiddleware2(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware2 process_request..." ) def process_response( self , request, response): print ( "CustomMiddleware2 process_response" ) return response |
访问index页面,控制台输出结果:
1 2 3 4 5 | CustomMiddleware process_request... CustomMiddleware2 process_request... index..... CustomMiddleware2 process_response CustomMiddleware process_response |
注意:1 .在process_request函数中,如果添加了return语句,会发生中断现象,程序把请求发给中间件,然后依次返回给请求者。
2.在process_response函数中,一定要有return语句,因为需要一层层往回传值给请求者(浏览器)。
(1)如果是在CustomerMiddleware类中的process_request函数中添加return HttpResponse("forbidden..."),输出如下:
1 2 3 4 5 6 | 网页显示: forbidden... 控制台输出: CustomMiddleware process_request... CustomMiddleware process_response |
(2)如果是在CustomerMiddleware2类中的process_request函数中添加return HttpResponse("forbidden..."),输出如下:
1 2 3 4 5 6 7 8 | 网页显示: forbidden... 控制台输出 CustomMiddleware process_request... CustomMiddleware2 process_request... CustomMiddleware2 process_response CustomMiddleware process_response |
流程图如下:
2、process_view
1 | def process_view( self , request, callback, callback_args, callback_kwargs):... |
my_middlewares.py修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class CustomerMiddleware(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware process_request..." ) def process_response( self , request, response): # 必须有返回值,要一层层往回传,不加就会报错 print ( "CustomMiddleware process_response" ) return response def process_view( self , request, callback, callback_args, callback_kwargs): print ( "CustomMiddleware1 process_view" ) class CustomerMiddleware2(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware2 process_request..." ) def process_response( self , request, response): print ( "CustomMiddleware2 process_response" ) return response def process_view( self , request, callback, callback_args, callback_kwargs): print ( "CustomMiddleware2 process_view" ) |
执行结果如下所示:
1 2 3 4 5 6 7 8 9 10 11 | 网页显示: index 控制台输出 CustomMiddleware process_request... CustomMiddleware2 process_request... CustomMiddleware1 process_view CustomMiddleware2 process_view index..... CustomMiddleware2 process_response CustomMiddleware process_response |
上述代码运行过程见下图:
当最后一个中间件的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户。
意义:在中间件走完时,多走了一步路由控制,而callback参数就是用来找到这次请求对应的视图函数(callback_args是视图函数的参数),因此可以提前一步来执行视图函数,而如果return直接返回,则可以跳过视图函数这一步直接返回。
示例1:用process_view来调用视图函数
仅修改CustomMiddleware2的process_view函数,修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class CustomerMiddleware2(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware2 process_request..." ) # return HttpResponse("forbidden...") def process_response( self , request, response): print ( "CustomMiddleware2 process_response" ) return response def process_view( self , request, callback, callback_args, callback_kwargs): print ( "====》" , callback(callback_args)) print ( "CustomMiddleware2 process_view" ) |
输出如下所示:
1 2 3 4 5 6 7 8 9 | CustomMiddleware process_request... CustomMiddleware2 process_request... CustomMiddleware1 process_view index..... = = = = 》 <HttpResponse status_code = 200 , "text/html; charset=utf-8" > CustomMiddleware2 process_view index..... CustomMiddleware2 process_response CustomMiddleware process_response |
示例2:给process_view函数返回值
仅修改CustomMiddleware2的process_view函数,修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class CustomerMiddleware2(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware2 process_request..." ) # return HttpResponse("forbidden...") def process_response( self , request, response): print ( "CustomMiddleware2 process_response" ) return response def process_view( self , request, callback, callback_args, callback_kwargs): # print("====》", callback(callback_args)) print ( "CustomMiddleware2 process_view" ) return HttpResponse( "123" ) |
输出如下所示:
1 2 3 4 5 6 7 8 9 10 | 页面显示: 123 控制台输出: CustomMiddleware process_request... CustomMiddleware2 process_request... CustomMiddleware1 process_view CustomMiddleware2 process_view CustomMiddleware2 process_response CustomMiddleware process_response |
注意:process_view如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。
3、process_exception
1 | def process_exception( self , request, exception):... |
示例修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class CustomerMiddleware(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware1 process_request..." ) def process_response( self , request, response): # 必须有返回值,要一层层往回传,不加就会报错 print ( "CustomMiddleware1 process_response" ) return response def process_view( self , request, callback, callback_args, callback_kwargs): print ( "CustomMiddleware1 process_view" ) def process_exception( self , request, exception): print ( "CustomMiddleware1 process_exception" ) class CustomerMiddleware2(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware2 process_request..." ) def process_response( self , request, response): print ( "CustomMiddleware2 process_response" ) return response def process_view( self , request, callback, callback_args, callback_kwargs): print ( "CustomMiddleware2 process_view" ) def process_exception( self , request, exception): print ( "CustomMiddleware2 process_exception" ) |
输出如下所示:
1 2 3 4 5 6 7 8 9 10 11 | 网页显示: Index 控制台输出: CustomMiddleware1 process_request... CustomMiddleware2 process_request... CustomMiddleware1 process_view CustomMiddleware2 process_view index..... CustomMiddleware2 process_response CustomMiddleware1 process_response |
注意:在代码正常情况下不会执行,当视图中报错时,才会依次执行process_excepsion和process_response。
当views出现错误时流程图如下所示:
(1)在视图函数中填写错误代码
1 2 3 4 | def index(request): print ( "index....." ) yuan return HttpResponse( "Index" ) |
输出如下:
1)页面显示错误提示
2)控制台输出
1 2 3 4 5 6 7 8 9 10 11 | CustomMiddleware1 process_request... CustomMiddleware2 process_request... CustomMiddleware1 process_view CustomMiddleware2 process_view Internal Server Error: / index / index..... CustomMiddleware2 process_exception CustomMiddleware1 process_exception 大段的错误提示 CustomMiddleware2 process_response CustomMiddleware1 process_response |
(2)在process_exception中抓取异常信息,返回到页面中显示
仅对CustomerMiddleware2中的process_exception修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class CustomerMiddleware2(MiddlewareMixin): def process_request( self , request): print ( "CustomMiddleware2 process_request..." ) # return HttpResponse("forbidden...") def process_response( self , request, response): print ( "CustomMiddleware2 process_response" ) return response def process_view( self , request, callback, callback_args, callback_kwargs): print ( "CustomMiddleware2 process_view" ) def process_exception( self , request, exception): print ( "CustomMiddleware2 process_exception" ) return HttpResponse(exception) |
输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 | 页面显示: name 'yuan' is not defined 控制台输出: CustomMiddleware1 process_request... CustomMiddleware2 process_request... CustomMiddleware1 process_view CustomMiddleware2 process_view index..... CustomMiddleware2 process_exception CustomMiddleware2 process_response CustomMiddleware1 process_response |
CustomMiddleware2的process_exception在捕获到错误后,把return值作为响应体直接返回了,就不再执行后面的exception了(CustomMiddleware1的),再依次传给process_response即返回给浏览器了。
三、中间件的应用
1、做IP访问频率限制
某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。
2、URL访问过滤
如果用户访问的是login视图(放过)
如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!
(1)在settings.py中定义白名单:
1 2 | # 白名单 WHITE_LIST = [ "/login/" , "/reg/" , "/logout/" ] |
(2)创建自定义的中间件文件./app01/my_middlewares.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse, redirect from authDemo import settings class AutoMiddleware(MiddlewareMixin): def process_request( self , request): # 拿到白名单 white_list = settings.WHITE_LIST if request.path in white_list: # 路径在白名单中直接通过 return None # return None等同于不写return ,中间件通过 # 不在白名单中的路径需要校验是否登录验证 if not request.user.is_authenticated: # 未经过校验跳转到登录页面 return redirect( "/login/" ) |
(3)在settings中添加自定义中间件
1 2 3 4 5 6 7 8 9 10 | MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware' , 'django.contrib.sessions.middleware.SessionMiddleware' , 'django.middleware.common.CommonMiddleware' , 'django.middleware.csrf.CsrfViewMiddleware' , 'django.contrib.auth.middleware.AuthenticationMiddleware' , 'django.contrib.messages.middleware.MessageMiddleware' , 'django.middleware.clickjacking.XFrameOptionsMiddleware' , "app01.my_middlewares.AutoMiddleware" , ] |
运行程序,并访问index页面,发现跳转到登录页面。
虽然中间件很方便,但不是所有情况下都应该用中间件,稍有不慎就会降低程序效率。往往视图函数大多数都需要校验,则使用中间件比较合适,只有少量需要校验则还是使用@login_required装饰器更加合适。
四、中间件源码试读
主要尝试着读以下两个自带的中间件:
1 2 | 'django.contrib.sessions.middleware.SessionMiddleware' , 'django.contrib.auth.middleware.AuthenticationMiddleware' , |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术