中间件是 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
Django 提供的 get_response
响应可能是实际视图(如果这是最后列出的中间件),或者它可能是链中的下一个中间件。不需要知道或关心当前的中间件到底是什么,它只是代表了下一步的内容。
以上是一个轻微的简化——链中最后一个中间件调用的 get_response
可不是实际视图,而是处理程序的包装方法,它负责应用 view middleware,调用具有适当URL参数的视图,并应用 template-response 和 exception 中间件。
中间件可以只支持同步Python(默认),或异步Python,或者二者都支持。查看 异步支持 来了解如何通知你支持的是什么以及如何知道你是哪种请求。
中间件可以放在 Python 路径上的任何地方。
__init__(get_response)
¶中间件工厂必须接受 get_response
参数。还可以初始化中间件的一些全局状态。记住两个注意事项:
get_response
参数初始化您的中间件,因此不能定义 __init__()
,因为需要其他参数。__call__()
method which is called once per request,
__init__()
is called only once, when the web server starts.在启动时确定是否应该使用一个中间件有时是有用的。在这些情况下,您的中间件的 __init__()
方法可能会引发 MiddlewareNotUsed
。Django 将从中间件进程中删除该中间件,并将调试消息记录到 django.request 日志:设置 DEBUG
为 True
。
若要激活中间件组件,请将其添加到 Django 设置中的 MIDDLEWARE
列表中。
在 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
后面运行 。中间件。Session中间件。请参阅 中间件顺序 ,用于一些关于 Django 中间件类排序的常见提示。
在请求阶段,在调用视图之前,Django 按照定义的顺序应用中间件 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
将阻止中间件之后运行的任何视图修改请求的上传处理程序 (modify the upload handlers for the request ),通常应该避免这样。
CsrfViewMiddleware
类可以被视为一个例外,因为它提供 csrf_exempt()
和 csrf_protect()
装饰器,它们允许视图完全控制 CSRF 验证在什么时候进行。
process_exception()
¶process_exception
(request, exception)¶request
是一个 HttpRequest
对象。 exception
是一个由视图函数引发的 Exception
对象。
当视图引发异常时,Django 会调用 process_exception()
。process_exception()
应该返回 None
或 HttpResponse
对象。如果它返回一个 HttpResponse
对象,模板响应和响应中间件将被应用且会将结果响应返回浏览器。否则,就会开始默认异常处理( default exception handling )。
再次,中间件在响应阶段会按照相反的顺序运行,其中包括 process_exception
。如果异常中间件返回一个响应,那么中间件之上的中间件类的 process_exception
方法根本不会被调用。
process_template_response()
¶process_template_response
(request, response)¶request
是一个 HttpRequest
对象。response
是 TemplateResponse
对象(或者等效对象),它通过 Django 视图或中间件返回。
process_template_response()
在视图被完全执行后调用,如果响应实例有 render()
方法,表明它是一个 TemplateResponse
或等效对象。
它必须返回一个实现了 render
方法的响应对象。它可以通过改变``response.template_name`` 和 response.context_data
来改变给定的 response
,或者它可以创建和返回全新的 TemplateResponse
或等效对象。
你不需要显式地渲染响应——一旦所有模板中间件被调用,响应会被自动渲染。
中间件会在响应阶段按照相反的顺序运行,其中包括 process_template_response()
。
与 HttpResponse
不同,StreamingHttpResponse
没有 content
属性。因此,中间件不能再假设所有响应有 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
allows both synchronous and
asynchronous iterators. The wrapping function must match. Check
StreamingHttpResponse.is_async
if your middleware needs to
support both types of iterator.
Support for streaming responses with asynchronous iterators was added.
Django 自动转换视图引发的异常,或者带有错误状态代码的特定 HTTP 响应内的中间件引发的异常。某些异常( Certain exceptions )被转换为 4xx 状态代码,而未知异常被转换为 500 状态代码。
这个变换发生在每个中间件的前后(你可以把它想象成洋葱每层中间的薄膜),因此每个中间件总是可以依赖于从调用它的 get_response
回调中获得某种类型的HTTP响应。中间件不需要担心它们对 get_response
的调用包装在 try/except
里,也不需要担心处理稍后的中间件或视图引发的异常。即使链中的下一个中间件引发了 Http404
异常,比如中间件不会查看异常,相反,它会得到一个带有404 status_code
的 HttpResponse
对象。
You can set DEBUG_PROPAGATE_EXCEPTIONS
to True
to skip this
conversion and propagate exceptions upward.
中间件支持同步和异步请求的任意组合。如果Django不能同时支持它们,它会调整请求来适应中间件的需求,但会有性能损失。
默认情况下,Django假设你的中间件只能处理同步请求。如果要改变这种模式,需要在你的中间件工厂函数或类中添加入如下属性:
sync_capable
是一个布尔值,来表明中间件是否处理同步请求。默认为 True
。async_capable
是一个布尔值,来表明中间件是否处理异步请求。默认为 False
。If your middleware has both sync_capable = True
and
async_capable = True
, then Django will pass it the request without
converting it. In this case, you can work out if your middleware will receive
async requests by checking if the get_response
object you are passed is a
coroutine function, using 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 会优化中间件调用栈,使其尽可能少的同步/异步转换。
因此,即使你包装的是一个异步视图,如果在你和视图之间有其他的、同步的中间件,你也可能会在同步模式下被调用。
When using an asynchronous class-based middleware, you must ensure that instances are correctly marked as coroutine functions:
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.utils.deprecation.
MiddlewareMixin
¶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
一起使用,__call__()
方法将永远不会被使用;Django 会直接调用 process_request()
和 process_response()
。
在大多数情况下,从这个 Mixin 中继承就足以使一个旧式中间件与新系统兼容,并具有足够的向后兼容性。新的短路语义对现有中间件无害甚至有益。在少数情况下,中间件类可能需要一些改变来适应新的语义。
MIDDLEWARE
和 MIDDLEWARE_CLASSES
在使用上有些行为差异:
MIDDLEWARE_CLASSES
下,每个中间件将始终调用它的 process_response
方法,即使早期的中间件通过从其 process_response
方法返回响应而短路。MIDDLEWARE
下,中间件行为更像洋葱:响应在输出时经过的层与在输入时看到请求的层相同。如果一个中间件短路,只有那个中间件和之前的中间件可以看到响应。MIDDLEWARE_CLASSES
下,process_exception
应用于中间件 process_request
方法引发的异常。在 MIDDLEWARE
下,process_exception
只应用于视图引发的异常(或者从 TemplateResponse
的 render
方法引发的异常)。中间件引发的异常被转换为合适的 HTTP 响应,然后传递到下一个中间件。MIDDLEWARE_CLASSES
下,如果 process_response
方法引发了异常,所有更早之前的中间件的 process_response
方法会被跳过,并一直返回 500 Internal Server Error
的 HTTP 响应(即使引发的异常是例如 Http404
)。在 MIDDLEWARE
,一个中间件引发的异常将立刻被转换为合适的 HTTP 响应,然后下一个中间件将看到响应。中间件不会因为中间件引发异常而被跳过。5月 12, 2023