执行顺序
按照settings.MIDDLEWARE的顺序,由上至下执行,如:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"apps.api.middleware_mgr.Middleware",
]
# django/core/handlers/base.py
class BaseHandler(object):
def load_middleware(self):
# _get_response会逐层调用中间件的process_view方法
# 然后处理view,即中间件的最中心
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
# 按照 MIDDLEWARE 的逆序生成中间件实例
middleware = import_string(middleware_path)
try:
# 这里将中间件实例当做函数调用,即MiddlewareMixin的__call__方法
# 由于是按逆序生成中间件实例,因此是一层层外套用
mw_instance = middleware(handler)
except MiddlewareNotUsed as exc:
...
if hasattr(mw_instance, 'process_view'):
# process_view是正序调用
self._view_middleware.insert(0, mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
# process_template_response 和 process_exception 都是逆序调用
self._template_response_middleware.append(mw_instance.process_template_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.append(mw_instance.process_exception)
# 这边将当前层的中间件实例作为外一层的get_response函数
# 传入MiddlwareMixin的__init__中
# 这样保证了process_request和process_response的正确调用顺序
handler = convert_exception_to_response(mw_instance)
中间件发生错误后的返回
中间件发生错误后将直接返回一个HttpResponse对象,下层中间件将不会被执行
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
# 当response不为空时,将不会执行下层中间件
if not response:
# 没有发生错误,继续执行下层中间件
response = self.get_response(request)
# 直接执行当前层的proces_response,并返回响应
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
自定义中间件
五大钩子函数
表头 | 表头 | 表头 | 表头 |
---|---|---|---|
process_request | 请求刚到来,执行视图之前 | 配置列表的正序 | None或者HttpResponse对象 |
process_response | 视图执行完毕,返回响应时 | 逆序 | HttpResponse对象 |
process_view | process_request之后,路由转发到视图,执行视图之前 | 正序 | None或者HttpResponse对象 |
process_exception | 视图执行中发生异常时 | 逆序 | None或者HttpResponse对象 |
process_template_response | 视图刚执行完毕,process_response之前 | 逆序 | 实现了render方法的响应对象 |
例子
注意middleware方法的输入参数都是固定的
# utils/my_middleware.py
from django.utils.deprecation import MiddlewareMixin
class MyMiddleware1(MiddlewareMixin):
def __str__(self):
return "MyMiddleware1"
def process_request(self, request):
print(self, " processing request...")
def process_view(self, request, view_func, view_args, view_kwargs):
print(self, " processing view...")
def process_response(self, request, response):
print(self, " processing response...")
return response
def process_exception(self, request, exception):
print(self, " processing exception...")
class MyMiddleware2(MiddlewareMixin):
def __str__(self):
return "MyMiddleware2"
def process_request(self, request):
print(self, " processing request...")
def process_view(self, request, view_func, view_args, view_kwargs):
print(self, " processing view...")
def process_response(self, request, response):
print(self, " processing response...")
return response
def process_exception(self, request, exception):
print(self, " processing exception...")
# 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',
'utils.my_middleware.MyMiddleware1',
'utils.my_middleware.MyMiddleware2'
]
# urls.py
from django.conf.urls import url
from demo.views import mid_test
urlpatterns = [
url(r"^midtest/", mid_test),
]
# views.py
from django.http import HttpResponse
def mid_test(request):
print('in mid_test view')
return HttpResponse('ok')
访问 http://127.0.0.1:8000/midtest/ ,可在控制台得到这样的输出,验证了之前关于中间件执行顺序的理论
MyMiddleware1 processing request...
MyMiddleware2 processing request...
MyMiddleware1 processing view...
MyMiddleware2 processing view...
in mid_test view
MyMiddleware2 processing response...
MyMiddleware1 processing response...
[08/Jun/2020 17:32:54] "GET /midtest/ HTTP/1.1" 200 2
中间件的中断
如果process_request返回了一个HttpResponse对象
class MyMiddleware1(MiddlewareMixin):
def __str__(self):
return "MyMiddleware1"
def process_request(self, request):
print(self, " processing request...")
return HttpResponse("break")
下层中间件将不会被执行,response会按原路返回
MyMiddleware1 processing request...
MyMiddleware1 processing response...
[08/Jun/2020 18:08:21] "GET /midtest/ HTTP/1.1" 200 5
如果process_view返回了一个HttpResponse对象,同样
class MyMiddleware1(MiddlewareMixin):
def __str__(self):
return "MyMiddleware1"
def process_request(self, request):
print(self, " processing request...")
return HttpResponse("break")
MyMiddleware1 processing request...
MyMiddleware2 processing request...
MyMiddleware1 processing view...
MyMiddleware2 processing response...
MyMiddleware1 processing response...
[08/Jun/2020 18:11:00] "GET /midtest/ HTTP/1.1" 200 5
视图异常
如果视图引发了异常,如
from django.http import HttpResponse
def mid_test(request):
print('in mid_test view')
1/0
return HttpResponse('ok')
则Django会逆序调用中间件的process_exception方法,紧接着逆序调用process_response方法
MyMiddleware1 processing request...
MyMiddleware2 processing request...
MyMiddleware1 processing view...
MyMiddleware2 processing view...
in mid_test view
MyMiddleware2 processing exception...
MyMiddleware1 processing exception...
Internal Server Error: /midtest/
Traceback (most recent call last):
File "/home/demo/venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/home/demo/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/demo/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/demo/views.py", line 6, in mid_test
1/0
ZeroDivisionError: division by zero
MyMiddleware2 processing response...
MyMiddleware1 processing response...
如果process_exception返回了一个HttpResponse对象
class MyMiddleware2(MiddlewareMixin):
def process_exception(self, request, exception):
print(self, " processing exception...")
return HttpResponse('not ok')
页面会显示process_exception返回的内容,而不再使用默认异常处理。并且,此中间件之上的中间件类的process_exception方法不会被调用。但是process_response方法仍然会被逆序调用
MyMiddleware1 processing request...
MyMiddleware2 processing request...
MyMiddleware1 processing view...
MyMiddleware2 processing view...
in mid_test view
MyMiddleware2 processing exception...
MyMiddleware2 processing response...
MyMiddleware1 processing response...
参考资料
Django中间件 --刘江
Middleware¶