中间件 — Django 文档
中间件
中间件是 Django 请求/响应处理的钩子框架。 它是一个轻量级的低级“插件”系统,用于全局更改 Django 的输入或输出。
每个中间件组件负责做一些特定的功能。 例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求相关联。
本文档解释了中间件的工作原理、如何激活中间件以及如何编写自己的中间件。 Django 附带了一些内置的中间件,您可以立即使用。 它们记录在 内置中间件参考 中。
编写自己的中间件
中间件工厂是一个可调用对象,它接受一个 get_response
可调用对象并返回一个中间件。 中间件是一个可调用的对象,它接受请求并返回响应,就像视图一样。
中间件可以写成一个函数,如下所示:
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
或者它可以写成一个实例可调用的类,如下所示:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Django 提供的 get_response
可调用对象可能是实际视图(如果这是最后列出的中间件),也可能是链中的下一个中间件。 当前的中间件不需要知道或关心它到底是什么,只需要它代表接下来发生的任何事情。
以上是一个轻微的简化 - 链中最后一个中间件可调用的 get_response
不是实际视图,而是来自处理程序的包装方法,它负责应用 视图中间件 ,使用适当的 URL 参数调用视图,并应用 template-response 和 exception 中间件。
中间件可以只支持同步 Python(默认),只支持异步 Python,或者两者都支持。 请参阅 异步支持 以了解如何宣传您支持的内容的详细信息,并了解您收到的请求类型。
中间件可以存在于 Python 路径上的任何位置。
__init__(get_response)
中间件工厂必须接受 get_response
参数。 您还可以为中间件初始化一些全局状态。 请记住几个注意事项:
- Django 仅使用
get_response
参数初始化您的中间件,因此您不能将__init__()
定义为需要任何其他参数。 - 与每个请求调用一次的
__call__()
方法不同,当 Web 服务器启动时,__init__()
仅被调用一次 '。
将中间件标记为未使用
在启动时确定是否应该使用中间件有时很有用。 在这些情况下,您的中间件的 __init__()
方法可能会引发 MiddlewareNotUsed。 然后,当 :setting:`DEBUG` 为 True
时,Django 将从中间件进程中删除该中间件,并将调试消息记录到 django.request 记录器。
激活中间件
要激活中间件组件,请将其添加到 Django 设置中的 :setting:`MIDDLEWARE` 列表中。
在 :setting:`MIDDLEWARE` 中,每个中间件组件都由一个字符串表示:中间件工厂类或函数名称的完整 Python 路径。 例如,这里是由创建的默认值 :djadmin:`django-admin startproject ` :
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',
]
Django 安装不需要任何中间件——:setting:`MIDDLEWARE` 可以为空,如果你愿意——但强烈建议你至少使用 CommonMiddleware。
:setting:`MIDDLEWARE` 中的顺序很重要,因为中间件可以依赖于其他中间件。 例如,AuthenticationMiddleware 在会话中存储经过身份验证的用户; 因此,它必须在 SessionMiddleware 之后运行。 有关 Django 中间件类排序的一些常见提示,请参阅 中间件排序 。
中间件顺序和分层
在请求阶段,在调用视图之前,Django 按照它在 :setting:`MIDDLEWARE` 中定义的顺序,自上而下地应用中间件。
你可以把它想象成一个洋葱:每个中间件类都是一个包裹视图的“层”,它位于洋葱的核心。 如果请求通过了洋葱的所有层(每个层都调用 get_response
将请求传递到下一层),一直到核心的视图,然后响应将通过每一层(以相反的顺序)在回来的路上。
如果其中一层决定短路并返回响应而不调用其 get_response
,则该层(包括视图)内的洋葱层都不会看到请求或响应。 响应只会通过请求传入的相同层返回。
其他中间件钩子
除了前面描述的基本请求/响应中间件模式之外,您还可以向基于类的中间件添加其他三个特殊方法:
process_view()
- process_view(request, view_func, view_args, view_kwargs)
request
是一个 HttpRequest 对象。 view_func
是 Django 即将使用的 Python 函数。 (它是实际的函数对象,而不是作为字符串的函数名称。) view_args
是将传递给视图的位置参数列表,而 view_kwargs
是关键字字典将传递给视图的参数。 view_args
和 view_kwargs
都不包含第一个视图参数 (request
)。
process_view()
在 Django 调用视图之前调用。
它应该返回 None
或 HttpResponse 对象。 如果它返回 None
,Django 将继续处理这个请求,执行任何其他 process_view()
中间件,然后执行适当的视图。 如果它返回一个 HttpResponse 对象,Django 就不会调用适当的视图; 它会将响应中间件应用于 HttpResponse 并返回结果。
笔记
在视图运行之前或在 process_view()
中访问中间件内部的 request.POST 将阻止在中间件能够修改请求的上传处理程序之后运行的任何视图,并且通常应该避免。
CsrfViewMiddleware 类可以被视为一个例外,因为它提供了 csrf_exempt() 和 csrf_protect() 装饰器,它们允许视图显式控制 CSRF应该进行验证。
process_exception()
- process_exception(request, exception)
request
是一个 HttpRequest 对象。 exception
是由视图函数引发的 Exception
对象。
当视图引发异常时,Django 调用 process_exception()
。 process_exception()
应返回 None
或 HttpResponse 对象。 如果它返回一个 HttpResponse 对象,则模板响应和响应中间件将被应用并将结果响应返回给浏览器。 否则, 默认异常处理 开始。
同样,中间件在响应阶段以相反的顺序运行,其中包括 process_exception
。 如果异常中间件返回响应,则根本不会调用该中间件之上的中间件类的 process_exception
方法。
process_template_response()
- process_template_response(request, response)
request
是一个 HttpRequest 对象。 response
是 Django 视图或中间件返回的 TemplateResponse 对象(或等效对象)。
process_template_response()
在视图执行完毕后立即调用,如果响应实例有 render()
方法,表明它是 TemplateResponse 或等效方法。
它必须返回一个实现 render
方法的响应对象。 它可以通过改变 response.template_name
和 response.context_data
来改变给定的 response
,或者它可以创建并返回一个全新的 TemplateResponse 或等效的。
您不需要显式呈现响应——一旦所有模板响应中间件都被调用,响应将自动呈现。
中间件在响应阶段以相反的顺序运行,包括 process_template_response()
。
处理流响应
与 HttpResponse 不同,StreamingHttpResponse 没有 content
属性。 因此,中间件不能再假设所有响应都具有 content
属性。 如果他们需要访问内容,他们必须测试流响应并相应地调整他们的行为:
if response.streaming:
response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
response.content = alter_content(response.content)
笔记
streaming_content
应该被假定为太大而无法保存在内存中。 响应中间件可以将它包装在一个新的生成器中,但不能使用它。 包装通常按如下方式实现:
def wrap_streaming_content(content):
for chunk in content:
yield alter_content(chunk)
异常处理
Django 自动将视图或中间件引发的异常转换为带有错误状态代码的适当 HTTP 响应。 某些异常被转换为4xx状态码,而未知异常被转换为500状态码。
这种转换发生在每个中间件之前和之后(你可以把它想象成洋葱每一层之间的薄膜),所以每个中间件总是可以依赖于从调用它的 中得到某种 HTTP 响应。 X241X] 可调用。 中间件不需要担心将它们对 get_response
的调用包装在 try/except
中并处理可能由后来的中间件或视图引发的异常。 例如,即使链中的下一个中间件引发 Http404 异常,您的中间件也不会看到该异常; 相反,它会得到一个 HttpResponse 对象,其 status_code 为 404。
您可以将 :setting:`DEBUG_PROPAGATE_EXCEPTIONS` 设置为 True
以跳过此转换并向上传播异常。
异步支持
3.1 版中的新功能。
中间件可以支持同步和异步请求的任意组合。 如果 Django 不能同时支持两者,则会调整请求以适应中间件的要求,但会降低性能。
默认情况下,Django 假定您的中间件只能处理同步请求。 要更改这些假设,请在中间件工厂函数或类上设置以下属性:
sync_capable
是一个布尔值,指示中间件是否可以处理同步请求。 默认为True
。async_capable
是一个布尔值,指示中间件是否可以处理异步请求。 默认为False
。
如果您的中间件同时具有 sync_capable = True
和 async_capable = True
,那么 Django 会将请求传递给它而不进行转换。 在这种情况下,您可以使用 asyncio.iscoroutinefunction()
检查传递的 get_response
对象是否为协程函数,从而确定您的中间件是否会接收异步请求。
django.utils.decorators
模块包含 sync_only_middleware()、async_only_middleware() 和 sync_and_async_middleware() 装饰器,允许您将这些标志应用于中间件工厂职能。
返回的可调用对象必须与 get_response
方法的同步或异步性质相匹配。 如果你有一个异步 get_response
,你必须返回一个协程函数(async def
)。
process_view
、process_template_response
和 process_exception
方法(如果提供)也应调整以匹配同步/异步模式。 但是,如果您不这样做,Django 将根据需要单独调整它们,但会增加性能损失。
以下是如何创建支持两者的中间件功能的示例:
import asyncio
from django.utils.decorators import sync_and_async_middleware
@sync_and_async_middleware
def simple_middleware(get_response):
# One-time configuration and initialization goes here.
if asyncio.iscoroutinefunction(get_response):
async def middleware(request):
# Do something here!
response = await get_response(request)
return response
else:
def middleware(request):
# Do something here!
response = get_response(request)
return response
return middleware
笔记
如果您声明一个同时支持同步和异步调用的混合中间件,您获得的调用类型可能与底层视图不匹配。 Django 将优化中间件调用堆栈,以尽可能减少同步/异步转换。
因此,即使您包装了一个异步视图,如果您和视图之间有其他同步中间件,您也可能在同步模式下被调用。
升级 Django 1.10 之前的样式中间件
- class django.utils.deprecation.MiddlewareMixin
Django 提供 django.utils.deprecation.MiddlewareMixin
来简化创建与 :setting:`MIDDLEWARE` 和旧的 MIDDLEWARE_CLASSES
兼容的中间件类,并支持同步和异步请求。 Django 包含的所有中间件类都与这两种设置兼容。
mixin 提供了一个 __init__()
方法,该方法需要一个 get_response
参数并将其存储在 self.get_response
中。
__call__()
方法:
- 调用
self.process_request(request)
(如果定义)。 - 调用
self.get_response(request)
从后面的中间件和视图中获取响应。 - 调用
self.process_response(request, response)
(如果定义)。 - 返回响应。
如果与 MIDDLEWARE_CLASSES
一起使用,则永远不会使用 __call__()
方法; Django 直接调用 process_request()
和 process_response()
。
在大多数情况下,从这个 mixin 继承就足以使旧式中间件与新系统兼容并具有足够的向后兼容性。 新的短路语义对现有的中间件是无害的,甚至是有益的。 在少数情况下,中间件类可能需要进行一些更改以适应新的语义。
这些是使用 :setting:`MIDDLEWARE` 和 MIDDLEWARE_CLASSES
之间的行为差异:
- 在
MIDDLEWARE_CLASSES
下,每个中间件将始终调用其process_response
方法,即使较早的中间件通过从其process_request
方法返回响应而短路。 在 :setting:`MIDDLEWARE` 下,中间件的行为更像一个洋葱:响应在输出时经过的层与在输入时看到请求的层相同。 如果中间件短路,则只有该中间件及其之前的 :setting:`MIDDLEWARE` 会看到响应。 - 在
MIDDLEWARE_CLASSES
下,process_exception
应用于从中间件process_request
方法引发的异常。 在 :setting:`MIDDLEWARE` 下,process_exception
仅适用于从视图(或从 TemplateResponse 的render
方法)引发的异常。 从中间件引发的异常被转换为适当的 HTTP 响应,然后传递给下一个中间件。 - 在
MIDDLEWARE_CLASSES
下,如果process_response
方法引发异常,则跳过所有早期中间件的process_response
方法并始终返回500 Internal Server Error
HTTP 响应(即使如果引发的异常是例如 Http404)。 在 :setting:`MIDDLEWARE` 下,从中间件引发的异常将立即转换为适当的 HTTP 响应,然后队列中的下一个中间件将看到该响应。 由于中间件引发异常,中间件永远不会被跳过。
3.1 版更改: 向 MiddlewareMixin
添加了对异步请求的支持。