一、中间件的概念
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
Django的中间件的定义:
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中查看
:
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就不用再注释这个中间件了。
手动引入一个中间件:
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)
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。
自定义中间件需要引入:
from django.utils.deprecation import MiddlewareMixin
在视图函数中:
def index(request):
print("index.....")
return HttpResponse("Index")
在自定义中间件my_middlewares.py中:
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页面,控制台输出结果:
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..."),输出如下:
网页显示:
forbidden...
控制台输出:
CustomMiddleware process_request...
CustomMiddleware process_response
(2)如果是在CustomerMiddleware2类中的process_request函数中添加return HttpResponse("forbidden..."),输出如下:
网页显示:
forbidden...
控制台输出
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware2 process_response
CustomMiddleware process_response
流程图如下:
2、process_view
def process_view(self, request, callback, callback_args, callback_kwargs):...
my_middlewares.py修改如下:
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")
执行结果如下所示:
网页显示:
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函数,修改如下:
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")
输出如下所示:
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函数,修改如下:
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")
输出如下所示:
页面显示:
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
def process_exception(self, request, exception):...
示例修改如下:
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")
输出如下所示:
网页显示:
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)在视图函数中填写错误代码
def index(request):
print("index.....")
yuan
return HttpResponse("Index")
输出如下:
1)页面显示错误提示
2)控制台输出
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修改:
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)
输出如下:
页面显示:
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中定义白名单:
# 白名单
WHITE_LIST = ["/login/", "/reg/", "/logout/"]
(2)创建自定义的中间件文件./app01/my_middlewares.py
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中添加自定义中间件
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装饰器更加合适。
四、中间件源码试读
主要尝试着读以下两个自带的中间件:
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',