中间件是 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__()
方法不同,当 Web 服务器启动时,__init__()
只被称为*一次*。在启动时确定是否应该使用一个中间件有时是有用的。在这些情况下,您的中间件的 __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中间件。请参阅 Middleware ordering ,用于一些关于 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)
Django 自动转换视图引发的异常,或者带有错误状态代码的特定 HTTP 响应内的中间件引发的异常。某些异常( Certain exceptions )被转换为 4xx 状态代码,而未知异常被转换为 500 状态代码。
这个变换发生在每个中间件的前后(你可以把它想象成洋葱每层中间的薄膜),因此每个中间件总是可以依赖于从调用它的 get_response
回调中获得某种类型的HTTP响应。中间件不需要担心它们对 get_response
的调用包装在 try/except
里,也不需要担心处理稍后的中间件或视图引发的异常。即使链中的下一个中间件引发了 Http404
异常,比如中间件不会查看异常,相反,它会得到一个带有404 status_code
的 HttpResponse
对象。
中间件支持同步和异步请求的任意组合。如果Django不能同时支持它们,它会调整请求来适应中间件的需求,但会有性能损失。
默认情况下,Django假设你的中间件只能处理同步请求。如果要改变这种模式,需要在你的中间件工厂函数或类中添加入如下属性:
sync_capable
是一个布尔值,来表明中间件是否处理同步请求。默认为 True
。async_capable
是一个布尔值,来表明中间件是否处理异步请求。默认为 False
。如果中间件的``sync_capable = True`` 并且 async_capable = True
,那么Django 会将请求传递给它而不进行转换。在这种情况下,你可以使用 asyncio.iscoroutinefunction()
来检查传递的 get_response
是否是一个协同函数,从而确定中间件是否接收到异步请求。
The django.utils.decorators
module contains
sync_only_middleware()
,
async_only_middleware()
, and
sync_and_async_middleware()
decorators that
allow you to apply these flags to middleware factory functions.
The returned callable must match the sync or async nature of the
get_response
method. If you have an asynchronous get_response
, you must
return a coroutine function (async def
).
process_view
, process_template_response
and process_exception
methods, if they are provided, should also be adapted to match the sync/async
mode. However, Django will individually adapt them as required if you do not,
at an additional performance penalty.
Here's an example of how to create a middleware function that supports both:
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
注解
If you declare a hybrid middleware that supports both synchronous and asynchronous calls, the kind of call you get may not match the underlying view. Django will optimize the middleware call stack to have as few sync/async transitions as possible.
Thus, even if you are wrapping an async view, you may be called in sync mode if there is other, synchronous middleware between you and the view.
django.utils.deprecation.
MiddlewareMixin
¶Django provides django.utils.deprecation.MiddlewareMixin
to ease creating
middleware classes that are compatible with both MIDDLEWARE
and the
old MIDDLEWARE_CLASSES
, and support synchronous and asynchronous requests.
All middleware classes included with Django are compatible with both settings.
The mixin provides an __init__()
method that requires a get_response
argument and stores it in 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 响应,然后下一个中间件将看到响应。中间件不会因为中间件引发异常而被跳过。Support for asynchronous requests was added to the MiddlewareMixin
.
10月 22, 2020