Django的中间件
学完cookie和session之后,我们学会了通过加装饰器判断用户登录状态,并且保持用户的登录状态,以便跳转至其他页面时取出登录用户的数据。
但是,我们通过给特定的视图函数加装饰器实现需求,而之后每添加一个视图函数可能都需要添加装饰器,这样就很烦!!!
接下来的内容,学会之后就能完美解决以上问题(实现类似给所有请求做相同操作),开始吧!
1. 初识中间件
1.1 定义
首先,官方版本走一遍:
- 中间件是一个用来处理Django的请求和响应的框架级别的钩子,是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
然后,小白版本走一遍:
- 中间件是介于request与response处理之间的一道处理过程(在视图函数执行之前和执行之后都可以做一些额外的操作),用于在全局范围内改变Django的输入和输出。它本质上就是一个自定义类,类中定义了几个方法,Django框架会在处理请求的特定的时间去执行这些方法。
**注意: 由于中间件影响的是全局,所以需要谨慎使用,使用不当会影响性能。 **
1.2 配置
中间件的英文,跟我大声念一遍:Middle--Ware!!!
是不是感觉似曾相识???
没错,我们在写BMS系统时其实就已经接触到了中间件。
当出现大黄页的时候........
当啥也不懂去百度乱搜一通,然后回到settings.py
注释的时候.........
想起来了吗???
算了,直接露脸吧!
#settings.py
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',
]
有的东西看上去是一个列表,一串串字符,但其实是一个个类,或者说是一个个中间件。
不信的话,Ctrl+Click试一下吧!
那么在之前,我们已经接触过一个CSRF相关的中间件了,就是那个被做(注释)掉的,哈哈开玩笑~
注释掉之后提交post请求的时候,就不会被forbidden了。
后来学会使用csrf_token
之后就不再注释这个中间件了。
那接下来就学习中间件中的方法以及这些方法什么时候被执行。
2. 自定义中间件
2.1 定义五个方法
分别是:
- 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
对象,则直接将该对象返回给用户。
示例:
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
2.2 process_request
参数:
request
- 这个request和视图函数中的request是一样的(在交给Django后面的路由之前,对这个request对象可以进行一系列的操作)。
- 由于request对象是一样的,所以我们可以对request对象进行一系列的操作,包括
request.变量名=变量值
,这样的操作,我们可以在后续的视图函数中通过相同的方式即可获取到我们在中间件中设置的值。
返回值:
-
None
- 返回值是None的话,按正常流程继续走,交给下一个中间件处理
-
HttpResponse
- 如果是HttpResponse对象,Django将不执行视图函数,而将相应对象返回给浏览器
执行顺序:
- 按照中间件的注册顺序正序执行,且最先执行
process_request
示例:
Step1>>>定义
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
Step2>>>注册
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',
'middlewares.MD1', # 自定义中间件MD1
'middlewares.MD2' # 自定义中间件MD2
]
Step3>>> 访问视图
MD1里面的 process_request
MD2里面的 process_request
app01 中的 index视图
Step4>>>调换Step2(M1/M2)注册顺序
MD2里面的 process_request
MD1里面的 process_request
app01 中的 index视图
总结:
- 调换顺序后,MD2比MD1先执行
process_request
- 视图函数最后执行
- MD1和MD2的
process_request
中的request
参数是同一个对象
2.3 process_response
参数:
request
- 上述例子中一样的对象
response
- 视图函数返回的HttpResponse对象
- 也就是说这是Django后台处理完之后给出一个的一个具体的视图
返回值:
HttpResponse
- 必须是,必须有!
执行顺序:
- 按照中间件的注册顺序逆序执行,process_request之后,视图函数之前
示例:
Step1>>>定义
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
def process_response(self, request, response):
print("MD2里面的 process_response")
return response
Step2>>>注册
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',
'middlewares.MD2', # 自定义中间件MD2
'middlewares.MD1' # 自定义中间件MD1
]
注意:此时注册顺序是先M2再M1!!!
Step3>>>访问视图
MD2里面的 process_request
MD1里面的 process_request
app01 中的 index视图
MD1里面的 process_response
MD2里面的 process_response
总结:
process_response
方法是在视图函数之后执行的,并且顺序是MD1比MD2先执行- M2的
process_response
方法首先执行,而它的process_response
方法最后执行
2.4 process_view
参数:
request
- 是
HttpRequest
对象。
- 是
view_func
- 是Django即将使用的视图函数
- 它是实际的函数对象,而不是函数的名称作为字符串
view_args
- 是将传递给视图的位置参数的列表
view_kwargs
- 是将传递给视图的关键字参数的字典
返回值:
-
None
-
如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后再执行相应的视图
-
HttpResponse
- 如果是HttpResponse对象,Django将不执行视图函数
- 而是直接在中间件中掉头,倒叙执行一个个
process_response
方法,最后返回给浏览器
执行顺序:
- Django路由系统之后,视图系统之前执行
- 按照中间件的注册顺序正序执行
示例:
Step1>>>定义
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD1 中的process_view")
print(view_func, view_func.__name__)
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
def process_response(self, request, response):
print("MD2里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD2 中的process_view")
print(view_func, view_func.__name__)
Step2>>>访问视图
MD2里面的 process_request
MD1里面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001DE68317488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001DE68317488> index
app01 中的 index视图
MD1里面的 process_response
MD2里面的 process_response
2.5 process_exception
参数:
request
- 是
HttpResponse
对象
- 是
exception
- 是视图函数异常产生的Exception对象
返回值:
-
None
- 如果返回一个None,则交给下一个中间件的
process_exception
方法来处理异常。
- 如果返回一个None,则交给下一个中间件的
-
HttpResponse
-
如果是HttpResponse对象,Django将调用模板和中间件中的
process_response
方法,并返回给浏览器,否则将默认处理异常。
-
执行顺序:
- 按照中间件的注册顺序的倒序执行
示例:
Step1>>>定义
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD1 中的process_view")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception)
print("MD1 中的process_exception")
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
def process_response(self, request, response):
print("MD2里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD2 中的process_view")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception)
print("MD2 中的process_exception")
Step2>>>制造异常,否则无法发挥process_exception
def index(request):
print("app01 中的 index视图")
raise ValueError("呵呵")
return HttpResponse("O98K")
Step3>>> 在MD1的process_exception中返回一个响应对象
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD1 中的process_view")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception)
print("MD1 中的process_exception")
return HttpResponse(str(exception)) # 返回一个响应对象
Step4>>>访问视图
MD2里面的 process_request
MD1里面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x0000022C09727488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x0000022C09727488> index
app01 中的 index视图
呵呵
MD1 中的process_exception
MD1里面的 process_response
MD2里面的 process_response
2.6 process_template_response(了解)
参数:
request
- 一个HttpRequest对象
response
- 是TemplateResponse对象(由视图函数或者中间件产生)
返回值:
-
render()
-
视图函数返回的对象有一个render()方法
-
或者表明该对象是一个TemplateResponse对象或等价方法
-
执行顺序:
- 在视图函数执行完成后立即执行
示例:
Step1>>>定义
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD1 中的process_view")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception)
print("MD1 中的process_exception")
return HttpResponse(str(exception))
def process_template_response(self, request, response):
print("MD1 中的process_template_response")
return response
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
def process_response(self, request, response):
print("MD2里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD2 中的process_view")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception)
print("MD2 中的process_exception")
def process_template_response(self, request, response):
print("MD2 中的process_template_response")
return response
Step2>>>视图函数
def index(request):
print("app01 中的 index视图")
def render():
print("in index/render")
return HttpResponse("O98K")
rep = HttpResponse("OK")
rep.render = render
return rep
Step3>>>访问视图
MD2里面的 process_request
MD1里面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001C111B97488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001C111B97488> index
app01 中的 index视图
MD1 中的process_template_response
MD2 中的process_template_response
in index/render
MD1里面的 process_response
MD2里面的 process_response
3. 中间件的应用场景
由于中间件工作在 视图函数执行前、执行后(像不像所有视图函数的装饰器!)适合所有的请求/一部分请求做批量处理
-
Django项目中默认启用了csrf保护,每次请求时通过CSRF中间件检查请求中是否有正确token值
-
当用户在页面上发送请求时,通过自定义的认证中间件,判断用户是否已经登陆,未登陆就去登陆。
-
当有用户请求过来时,判断用户是否在白名单或者在黑名单里
-
做IP限制
- 放在中间件类的列表中,阻止某些IP访问
- URL访问过滤
-
如果用户访问的是login视图(放过)
-
如果访问其他视图(需要检测是不是有session已经有了放行,没有返回login),这样就省得在多个视图函数上写装饰器了!
- 缓存
-
还记得CDN吗???就是我们在画请求生命周期的那张有粑粑的图的时候,哈哈哈kidding!
-
客户端请求来了,中间件去缓存看看有没有数据,有直接返回给用户,没有再去逻辑层执行视图函数
不行了,今天的内容实在是好多好多,欢迎在评论区留下你的赞,我们下篇见:)