中间件是插在 Django 的请求和响应过程之中的框架。这是一种轻量级的低层插件系统,用于全局调整 Django的输入或输出。
在django中,中间件其实就是一个类。在http请求到达视图函数之前和视图函数return之后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
中间件的执行流程
1、执行完所有的request方法 到达视图函数。
2、执行中间件的其他方法
3、经过所有response方法 返回客户端。
一、自定义中间件
1.在project下随便创建一个M.py文件
from django.utils.deprecation import MiddlewareMixin
class Middle1(MiddlewareMixin):
def process_request(self,request):
print(" process_reques")
def process_response(self, request,response):
print('process_response')
2、在setings文件中 注册这个 py文件
MIDDLEWARE = [
............
'django.middleware.cache.FetchFromCacheMiddleware',
'M1.Middle1'
]
process_request
process_response
Internal Server Error: /books/search/
Traceback (most recent call last):
File "D:\env_django_core\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "D:\env_django_core\lib\site-packages\django\utils\deprecation.py", line 93, in __call__
response = self.process_response(request, response)
File "D:\env_django_core\lib\site-packages\django\middleware\clickjacking.py", line 26, in process_response
if response.get('X-Frame-Options') is not None:
AttributeError: 'NoneType' object has no attribute 'get'
因为 自定义的中间件response方法没有return,交给下一个中间件,导致http请求中断了!!!
注意 自定义的中间件request 方法不要return 因为返回值中间件不再往下执行,导致 http请求到达不了视图层,因为request在视图之前执行!
正确写法:
from django.utils.deprecation import MiddlewareMixin
class Middle1(MiddlewareMixin):
# 不用return Django内部自动帮我们传递
# 或者return None 会继续执行剩余中间件
def process_request(self,request):
print("process_request")
def process_response(self, request,response):
print('process_response')
return response #执行完了这个中间件一定要 传递给下一个中间件
二、中间件执行顺序
上图中错误:
1、就是在process_request中有返回值的时候,是从当前的中间件的response开始返回的。
2、路由系统是在process_view之前执行的的,因为process_view要拿到需要执行视图函数的信息。
process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
MIDDLEWARE = [
............
'django.middleware.cache.FetchFromCacheMiddleware',
'M1.Middle',
'M2.Middle',
'M3.Middle',
]
1、process_request(self,request)
一般不返回值或者返回None。如果有返回值,直接跳转到当前中间件的process_response,依次执行后面的process_response。
# M2 process_request 有返回值
def process_request(self,request):
print("M2: process_request")
return HttpResponse('123', status=200)
# 略过所有的其他方法,直接跳转到当前中间件的process_response。
# M3的process_response被跳过
M1: process_request
M2: process_request
# 没有执行视图函数
M2: process_response
M1: process_response
2、 process_view(self, request, callback, callback_args, callback_kwargs)方法介绍
(1)执行完所有中间件的request方法
(2)url匹配成功
(3)拿到 视图函数的名称、参数,(注意不执行) 再执行process_view()方法
(4)最后去执行视图函数
def process_view(self, request,callback,callback_args,callback_kwargs ):
print("M1: process_view")
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_response
M2: process_response
M1: process_response
既然 process_view 拿到视图函数的名称、参数,(不执行) 再执行process_view()方法,最后才去执行视图函数!
那可以在 执行process_view环节直接 把函数执行返回吗?
def process_view(self, request,callback,callback_args,callback_kwargs ):
print("M2.process_view")
response=callback(request,*callback_args,**callback_kwargs)
return response
M1.process_view->M2.process_view->M1.process_response
跳过了M3的view直接返回了。
#
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
执行view视图函数
M3: process_response
M2: process_response
M1: process_response
3、process_exception(self, request, exception)方法
def process_exception(self, request, exception):
print('M1: process_exception')
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_response
M2: process_response
M1: process_response
并没有执行process_exception, 因为rocess_exception默认不执行,process_exception方法在视图函数执行出错的时候才会执行。
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_exception
M2: process_exception
M1: process_exception
Internal Server Error: /books/search/
Traceback (most recent call last):
File "D:\env_django_core\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "D:\env_django_core\lib\site-packages\django\core\handlers\base.py", line 126, in _get_response
response = self.process_exception_by_middleware(e, request)
File "D:\env_django_core\lib\site-packages\django\core\handlers\base.py", line 124, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\django_core_mysite\books\views.py", line 12, in search
a = b
NameError: name 'b' is not defined
M3: process_response
M2: process_response
M1: process_response
[08/May/2019 18:14:14] "GET /books/search/ HTTP/1.1" 500 68104
(1) 执行完所有 request 方法
(2) 执行 所有 process_view方法
(3) 如果视图函数出错,执行process_exception(最终response,process_exception的return值)。如果process_exception 方法有了 返回值 就不再执行 其他中间件的 process_exception,直接执行response方法响应
(4) 执行所有response方法
(5) 最后返回process_exception的返回值
def process_exception(self, request, exception):
print('M2: process_exception')
return HttpResponse('M2: process_exception')
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_exception
M2: process_exception # M2的 process_exception 有返回值
# M1 的 process_exception没执行, 直接到process_response
M3: process_response
M2: process_response
M1: process_response
浏览器显示 M2: process_exception
3、process_template_response()
如果视图函数中的返回值 中有render方法,才会执行 process_template_response
def process_template_response(self,request,response):
print('M2process_template_response')
return response
# 视图函数
def search(request):
print('执行view视图函数')
return render(request, 'search.html', {'error': error})
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
# 并没有执行process_template_response,
# 因为 视图函数返回的对象并没有 render()方法
M3: process_response
M2: process_response
M1: process_response
[09/May/2019 11:37:56] "GET /books/search/ HTTP/1.1" 200 4274
视图函数返回的对象有render() 方法:
from django.shortcuts import render,HttpResponse
# Create your views here.
class Foo():
def __init__(self,requ):
self.req = requ
def render(self):
return HttpResponse(“I'm render!”)
def index(request):
print("执行view视图函数")
obj = Foo(request)
return obj
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_template_response
M2: process_template_response
M1: process_template_response
M3: process_response
M2: process_response
M1: process_response
[09/May/2019 11:41:43] "GET /books/search/ HTTP/1.1" 200 12
浏览器显示: 'I’m render!
把对象的render方法的返回值返回给用户(注意不返回视图函数的return的结果了,而是返这个结果对象里render方法的返回值,并且process_template_response方法也要返回response不然会报错。
可以这么玩:
如果多个view需要处理类似的信息,可以在process_template_response对msg进行统一处理。
class Foo():
def __init__(self, requ, res, msg):
self.req = requ
self.res = res
self.msg = msg
def deal_with_msg(self):
pass
def render(self):
return self.res
def search(request):
print("执行view视图函数")
error = False
res = render(request, 'search.html', {'error': error})
msg = ''
obj = Foo(request, res, msg)
return obj
三、中间件应用场景
由于中间件工作在 视图函数执行前、执行后(像不像所有视图函数的装饰器!)适合所有的请求/一部分请求做批量处理
1、做IP限制
放在 中间件类的列表中,阻止某些IP访问了;
2、URL访问过滤
如果用户访问的是login视图(放过)
如果访问其他视图(需要检测是不是有session已经有了放行,没有返回login),这样就省得在 多个视图函数上写装饰器了!
3、缓存
客户端请求来了,中间件去缓存看看有没有数据,有直接返回给用户,没有再去逻辑层 执行视图函数
四、中间件钩子函数
定义一个中间件工厂函数,然后返回一个可以别调用的中间件。中间件工厂函数需要接收一个可以调用的get_response对象。返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。
def simple_middleware(get_response):
# 此处编写的代码仅在Django第一次配置和初始化的时候执行一次, 类似before_first_request
def middleware(request):
# 此处编写的代码会在每个请求处理视图前被调用, 类似before_request
response = get_response(request)
# 此处编写的代码会在每个请求处理视图之后被调用,类似after_request
return response
return middleware
例如,在子应用users应用中新建一个middleware.py文件,
def my_middleware(get_response):
print('init 被调用')
def middleware(request):
print('before request 被调用')
response = get_response(request)
print('after response 被调用')
return response
return 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',
'users.middleware.my_middleware', # 添加中间件
]
在请求视图被处理前,中间件由上至下依次执行
在请求视图被处理后,中间件由下至上依次执行
def my_middleware(get_response):
print('init 被调用')
def middleware(request):
print('before request 被调用')
response = get_response(request)
print('after response 被调用')
return response
return middleware
def my_middleware2(get_response):
print('init2 被调用')
def middleware(request):
print('before request 2 被调用')
response = get_response(request)
print('after response 2 被调用')
return response
return middleware
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',
'users.middleware.my_middleware', # 添加
'users.middleware.my_middleware2', # 添加
]
执行结果
init2 被调用
init 被调用
before request 被调用
before request 2 被调用
view 视图被调用
after response 2 被调用
after response 被调用
https://www.cnblogs.com/buyisan/p/8557252.html
https://www.cnblogs.com/skaarl/p/9457017.html?utm_source=debugrun&utm_medium=referral