中间件是连接到Django请求/响应处理的框架。它是一个轻的,低级的“插件”系统,用于全局更改Django的输入或输出。
每个中间件组件负责执行一些特定的功能。例如,django包含一个中间件组件, AuthenticationMiddleware
将用户与使用会话的请求关联起来。
本文介绍中间件的工作原理、如何激活中间件以及如何编写自己的中间件。Django附带一些内置中间件,您可以直接使用。它们被记录在 built-in middleware reference .
中间件工厂是一个可调用的 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
这个 get_response
Django提供的可调用可能是实际视图(如果这是最后列出的中间件),也可能是链中的下一个中间件。当前的中间件不需要知道或关心它到底是什么,只需要它表示接下来会发生什么。
以上只是一个小小的简化 get_response
链中最后一个中间件的可调用不是实际视图,而是处理程序中负责应用的包装方法 view middleware ,使用适当的URL参数调用视图,并应用 template-response 和 exception 中间件。
中间件可以仅支持同步Python(默认)、仅支持同步Python,或者两者都支持。看到 异步支持 了解如何宣传您支持的内容的详细信息,并了解您收到的请求类型。
中间件可以在您的python路径上的任何地方生存。
__init__(get_response)
¶中间件工厂必须接受 get_response
参数。您还可以初始化中间件的一些全局状态。记住几个注意事项:
Django只使用 get_response
参数,因此无法定义 __init__()
需要任何其他论证。
不像 __call__()
方法,每个请求调用一次, __init__()
仅被调用 once ,当Web服务器启动时。
在启动时确定是否应该使用中间件有时很有用。在这些情况下,您的中间件 __init__()
方法可能会提高 MiddlewareNotUsed
. 然后Django将从中间件进程中删除该中间件,并将调试消息记录到 django.request 记录器何时 DEBUG
是 True
.
要激活中间件组件,请将其添加到 MIDDLEWARE
在Django设置中列出。
在 MIDDLEWARE
,每个中间件组件都由一个字符串表示:中间件工厂类或函数名的完整python路径。例如,这里是由 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安装不需要任何中间件- MIDDLEWARE
可以是空的,如果你愿意的话-但强烈建议你至少使用 CommonMiddleware
.
秩序 MIDDLEWARE
重要的是,中间件可以依赖于其他中间件。例如, AuthenticationMiddleware
将经过身份验证的用户存储在会话中;因此,它必须在 SessionMiddleware
. 见 中间件排序 有关django中间件类排序的一些常见提示。
在请求阶段,在调用视图之前,Django按照定义的顺序应用中间件。 MIDDLEWARE
自上而下。
您可以把它想象成一个洋葱:每个中间件类都是一个“层”,它包装了视图,而视图位于洋葱的核心。如果请求通过洋葱的所有层(每个层调用 get_response
为了将请求传递到下一层),一直传递到核心的视图,然后响应将在返回的过程中通过每一层(按相反的顺序)。
如果其中一个层决定短路并返回响应而不调用 get_response
,该层(包括视图)内的洋葱层都不会看到请求或响应。响应将只通过请求传入的同一层返回。
除了前面描述的基本请求/响应中间件模式外,还可以向基于类的中间件添加其他三种特殊方法:
process_view()
¶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
并返回结果。
备注
访问 request.POST
在视图运行之前或在 process_view()
将阻止在中间件之后运行的任何视图 modify the upload handlers for the request ,通常应避免。
这个 CsrfViewMiddleware
类可以被视为异常,因为它提供了 csrf_exempt()
和 csrf_protect()
允许视图显式控制CSRF验证应该在什么时候发生的修饰符。
process_exception()
¶request
是一个 HttpRequest
对象。 exception
是一个 Exception
视图函数引发的对象。
Dango调用 process_exception()
当视图引发异常时。 process_exception()
应该返回 None
或 HttpResponse
对象。如果它返回 HttpResponse
对象,将应用模板响应和响应中间件,并将结果响应返回到浏览器。否则, default exception handling 踢进去。
同样,中间件在响应阶段以相反的顺序运行,其中包括 process_exception
. 如果异常中间件返回响应,则 process_exception
上面中间件类的方法根本不会被调用。
process_template_response()
¶request
是一个 HttpRequest
对象。 response
是 TemplateResponse
对象(或等效对象),由Django视图或中间件返回。
process_template_response()
如果响应实例具有 render()
方法,指示它是 TemplateResponse
或等同的。
它必须返回实现 render
方法。它可以改变给定的 response
通过改变 response.template_name
和 response.context_data
或者它可以创建并返回一个全新的 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)
StreamingHttpResponse
允许同步和异步迭代器。包装函数必须匹配。检查 StreamingHttpResponse.is_async
如果您的中间件需要支持这两种类型的迭代器。
Django自动将视图或中间件引发的异常转换为具有错误状态代码的适当HTTP响应。 Certain exceptions 转换为4xx状态代码,而未知异常转换为500状态代码。
这种转换发生在每个中间件之前和之后(您可以将其视为洋葱每层之间的薄膜),因此每个中间件始终可以依赖于从调用其 get_response
可赎回的。中间件不需要担心将其调用包装到 get_response
在一个 try/except
以及处理一个可能是由后来的中间件或视图引发的异常。即使链中的下一个中间件 Http404
异常,例如,您的中间件不会看到该异常;相反,它将得到一个 HttpResponse
对象与A status_code
404。
您可以设置 DEBUG_PROPAGATE_EXCEPTIONS
至 True
跳过此转换并向上传播异常。
中间件可以支持同步和同步请求的任何组合。如果Django无法同时支持两者,Django将调整请求以满足中间件的要求,但会带来性能损失。
默认情况下,Django假设您的中间件仅能够处理同步请求。要更改这些假设,请在中间件工厂函数或类上设置以下属性:
sync_capable
是一个布尔值,指示中间件是否可以处理同步请求。默认为 True
。
async_capable
是一个布尔值,指示中间件是否可以处理同步请求。默认为 False
。
如果您的中间件两者兼而有之 sync_capable = True
和 async_capable = True
,则Django将不转换它而将请求传递给它。在这种情况下,您可以通过检查您的中间件是否会接收异步请求来确定 get_response
对象是一个协程函数,使用 asgiref.sync.iscoroutinefunction
。
这个 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将根据需要单独调整它们,但会受到额外的性能处罚。
以下是如何创建支持两者的中间件功能的示例::
from asgiref.sync import iscoroutinefunction
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 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将优化中间件调用栈,以尽可能少地同步/同步转换。
因此,即使您正在包装Inbox视图,如果您和视图之间存在其他同步中间件,也可能会以同步模式调用您。
在使用基于异步类的中间件时,必须确保实例被正确标记为协程函数:
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
class AsyncMiddleware:
async_capable = True
sync_capable = False
def __init__(self, get_response):
self.get_response = get_response
if iscoroutinefunction(self.get_response):
markcoroutinefunction(self)
async def __call__(self, request):
response = await self.get_response(request)
# Some logic ...
return response
Django提供 django.utils.deprecation.MiddlewareMixin
为了轻松创建与两者兼容的中间件类 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
, the __call__()
永远不会使用方法;Django调用 process_request()
和 process_response()
直接。
在大多数情况下,从这个混合继承就足以使旧样式的中间件与具有足够向后兼容性的新系统兼容。新的短路语义将对现有中间件无害甚至有益。在一些情况下,中间件类可能需要一些更改来适应新的语义。
这些是使用 MIDDLEWARE
和 MIDDLEWARE_CLASSES
:
下 MIDDLEWARE_CLASSES
每个中间件都将 process_response
方法,即使早期的中间件通过返回其 process_request
方法。下 MIDDLEWARE
中间件的行为更像洋葱:响应在退出时经过的层与在进入时看到请求的层相同。如果一个中间件短路,那么只有那个中间件和它前面的那些 MIDDLEWARE
将看到响应。
下 MIDDLEWARE_CLASSES
, process_exception
应用于从中间件引发的异常 process_request
方法。下 MIDDLEWARE
, process_exception
仅适用于从视图(或从 render
A方法 TemplateResponse
)从中间件引发的异常被转换为适当的HTTP响应,然后传递到下一个中间件。
下 MIDDLEWARE_CLASSES
,如果 process_response
方法引发异常, process_response
跳过所有早期中间件的方法,并 500 Internal Server Error
始终返回HTTP响应(即使引发的异常是 Http404
)下 MIDDLEWARE
,从中间件引发的异常将立即转换为适当的HTTP响应,然后行中的下一个中间件将看到该响应。中间件从不被跳过,因为中间件引发了异常。
7月 22, 2024