中间件是什么
中间件是django框架提供的一套钩子机制,用于在请求和响应处理过程中插入自定义逻辑。它可以在全局范围内修改 Django 的输入或输出。
没有中间件的处理流程:http请求 ---> view处理函数 ---> http响应。
加上中间件钩子后的处理流程:http请求 --->中间件(process_request)---> view处理函数 --->中间件(process_response)---> http响应。
在一些场景,如记录日志、限制访问IP等操作,就可以用中间件钩子去解决。我们只需要把记录日志的动作封装成一个中间件,就可以在请求到达view之前记录日志了。
TIPs: 中间件是django框架的机制,DRF中并没有做改写。
中间件如何配置
通过settings.py文件里的MIDDLEWARE列表。
我们看一下新建一个django项目默认的配置。
python
# 每一行就是一个中间件,具体作用可以自行百度
# 可以根据需要,增加或删除需要使用的中间件
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',
]
自定义中间件
定义中间件有多重方法,可以基于函数,也可以基于类,下面只介绍一种推荐的方式。
python
# 基于类的混入方式
from django.utils.deprecation import MiddlewareMixin
# 自定义中间件名字
# 代码文件:myproject/middleware.py
class MyXyzMiddleware(MiddlewareMixin):
# 正常情况下返回None
# 异常情况返回response实例
# 比如ip白名单校验,通过则return,不通过返回response实例
def process_request(self, request):
print("Processing request", id(request))
# 返回response实例
def process_response(self, request, response):
print("Processing response", id(request))
return response
# 正常情况下返回None
# 异常情况可以返回response实例
def process_view(self, request, view_func, view_args, view_kwargs):
print("Processing view", view_func.__name__)
def process_exception(self, request, exception):
print("Processing exception", exception)
return HttpResponse("An error occurred")
def process_template_response(self, request, response):
print("process_template_response")
return response
# 将自定义中间件注册到settings文件中
MIDDLEWARE = [
...... 省略默认的
'myproject.middleware.MyXyzMiddleware'
]
在自定义中间件MyXyzMiddleware类里定义了5个函数,其实是中间件支持的5个钩子位置。实际开发中可以根据需要使用一个或者任意个函数,不需要的函数直接删掉就行。最常用的就是process_request和process_response。
中间件的执行顺序
先只考虑只有process_request和process_response的情况。
建设setttings文件里的配置如下:
python
MIDDLEWARE = [
'mw1',
'mw2',
'mw3'
]
那么正常情况下的请求处理逻辑是这样的.

可以看出中间件的执行是有顺序的,在请求阶段,按列表顺序从上到下执行,在响应阶段,是从下到上执行。因此对于中间件之间有依赖的,要在配置的时候注意顺序位置。
比如'django.contrib.auth.middleware.AuthenticationMiddleware'依赖'django.contrib.sessions.middleware.SessionMiddleware',就必需放在session的下面。
代码解析
上面关于中间件的说明都只是关注一些主要的流程。其实很多细节还是需要解析代码才可以理解,比如process_request和process_view的执行时机和退出时机,中间件这种执行顺序是如何进行编码设计的等等。
python
# 中间件的加载
# django/core/handlers/base.py
class BaseHandler:
# 列表,按顺序存储中间件里的process_view函数
_view_middleware = None
# 列表,按顺序存储中间件里的process_view函数
_template_response_middleware = None
# 列表,按顺序存储中间件里的process_view函数
_exception_middleware = None
# 函数,这个函数是一个层层嵌套的函数链条,一个函数包一个函数,组成了一个洋葱式的结构
_middleware_chain = None
# 下面的代码解析之保留了主要框架,去掉了关于异步的很多处理
def load_middleware(self, is_async=False):
# 初始的handler就是get_response函数
handler = get_response
# 对中间件列表先反转reversed,再循环遍历
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
# 这里handler函数作为一个参数传给了中间件
# 中间件这里的处理逻辑类似装饰函数,传入一个handler进行实例化
# 具体逻辑见后面关于中间件类的解析
mw_instance = middleware(handler)
# 分别将中间件的三个处理钩子塞进列表
if hasattr(mw_instance, "process_view"):
self._view_middleware.insert(0, mw_instance.process_view)
if hasattr(mw_instance, "process_template_response"):
self._template_response_middleware.append(mw_instance.process_template_response)
if hasattr(mw_instance, "process_exception"):
self._exception_middleware.append(mw_instance.process_exception)
# 将中间件的实例作为下一次循环的输入
handler = mw_instance
# 将经过所有中间件包装后的处理函数绑定到_middleware_chain
self._middleware_chain = handler
# 中间件类django/utils/deprecation.py
class MiddlewareMixin:
# 传入一个函数或可调用实例,把它绑定到self.get_response
# 这里需要注意get_response只是一个名称,
# 并不是django.core.handlers.base.py中的get_response函数
# 在load_middleware中,get_response实际是中间件实例mw_instance
def __init__(self, get_response):
self.get_response = get_response
super().__init__()
# 此处定义call魔法函数,每次调用的时候执行此段逻辑。
# 关于这段代码如何把一串中间件封装成洋葱式的函数,确实不太好语言表达
# 只能自己仔细揣摩了
def __call__(self, request):
response = None
if hasattr(self, "process_request"):
response = self.process_request(request)
# 这是一个短路写法,如果response为None,则继续执行get_response
# 如果response不为None,则直接进入了中间件的process_response部分
# 这就是如果一个中间件返回结果不是None,则直接跳过后面中间件和view视图处理,
# 进入process_reponse环节
response = response or self.get_response(request)
if hasattr(self, "process_response"):
response = self.process_response(request, response)
return response
# 洋葱函数的调用
# django/core/handlers/base.py
def get_response(self, request):
set_urlconf(settings.ROOT_URLCONF)
# 此处调用洋葱函数,在洋葱函数的中间层是调用视图的函数_get_response
response = self._middleware_chain(request)
return response
# 视图处理函数
# 如果中间件的process_request都通过了,则进入试图处理函数,就是下面的_get_response
# django/core/handlers/base.py
def _get_response(self, request):
response = None
# 解析路由,找到对应的处理函数
callback, callback_args, callback_kwargs = self.resolve_request(request)
# 顺序执行中间件中的process_view函数
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, callback_args, callback_kwargs)
# 若process_view任何一个函数返回非None,则跳过后续中间件
if response:
break
# 如果process_view中返回非None,也会跳过视图函数的执行
if response is None:
wrapped_callback = self.make_view_atomic(callback)
# 执行视图函数
response = wrapped_callback(request, *callback_args, **callback_kwargs)
# 返回响应结果
# 中间也有_template_response_middleware\process_exception_by_middleware处理的逻辑
# 不展开说明
return response
最后总结一张整体的流程处理图
