中间件是一个 hook 框架,它们可以介入 Django 的请求和响应处理过程。 它是一个轻量级、底层的“插件”系统,用于在全局修改Django 的输入或输出。
每个中间件组件负责完成某个特定的功能。 例如,Django 包含的一个中间件组件AuthenticationMiddleware
,它使用会话将用户和请求关联起来。
这篇文档讲解中间件如何工作、如何激活中间件以及如何编写你自己的中间件。 Django集成了一些内置的中间件可以直接开箱即用。 它们被归档在 内置中间件参考.
引入了新的中间件风格,用于新的MIDDLEWARE
设置。 如果您使用旧的MIDDLEWARE_CLASSES
设置,则在使用新设置之前,您需要adapt old,
custom middleware。
本文档介绍新型中间件。 有关旧式中间件的工作原理的说明,请参阅旧版本的文档中的此页。
一个中间件工厂是一个可调用对象,它采用一个get_response
可调用对象并返回一个中间件。 中间件是一个可调用的对象,它接受请求并返回响应,就像视图一样。
中间件可以写成一个如下所示的函数:
def simple_middleware(get_response):
# 一次性配置和初始化。
def middleware(request):
# 在调用视图(以及稍后的中间件)之前
# 要为每个请求执行代码。
response = get_response(request)
# 为每个请求/响应执行的代码
# 在调用视图之后
return response
return middleware
或者它可以写成一个类,其实例是可调用的,如下所示:
class SimpleMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# 一次性配置和初始化。
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参数调用视图,并应用模板响应和异常中间件。
中间件可以存在在你的Python路径的任何地方。
__init__(get_response)
¶中间件工厂必须接受get_response
参数。 您也可以初始化中间件的全局状态。 记住几个注意事项:
get_response
参数初始化您的中间件,因此您不能将__init__()
定义为需要任何其他参数。__call__()
方法不同,当Web服务器启动时,__init__()
仅被调用一次。在旧版本中,在Web服务器响应其第一个请求之前,才调用__init__()
。
在旧版本中,__init__()
不接受任何参数。 为了让您的中间件在Django 1.9及更早版本中使用,请使get_response
可选参数(get_response=None
)。
在启动时确定是否应该使用中间件是有用的。 在这些情况下,您的中间件的__init__()
方法可能会引发MiddlewareNotUsed
。 然后,当DEBUG
为True
时,Django将从中间件进程中删除该中间件,并将调试消息记录到django.request记录器。
要激活中间件组件,请将其添加到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
之后运行。 一些关于Django中间件类的顺序的常见提示,请见Middleware ordering。
在请求阶段,在调用视图之前,Django以MIDDLEWARE
(自上而下)定义的顺序应用中间件。
你可以像洋葱一样想起来:每个中间件类都是一个“层”,它覆盖了洋葱核心的视图。 如果请求通过洋葱的所有层(每个调用get_response
将请求传递到下一层),一直到核心的视图,响应将通过在每一层(以相反的顺序)的路上退出。
如果其中一个层决定短路并返回响应而不调用其get_response
,那么该层(包括视图)内的洋葱层都不会看到请求或响应。 响应将只返回通过请求传递的相同的层。
除了前面描述的基本请求/响应中间件模式,您还可以向基于类的中间件添加三种其他特殊方法:
process_view()
¶process_view
(request,view_func,view_args,view_kwargs)/ T5>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()
来处理。
None
应该返回一个process_exception()
或者一个HttpResponse
对象。 如果它返回一个HttpResponse
对象,则将应用模板响应和响应中间件,并将生成的响应返回给浏览器。 否则,default exception handling开始。
再次提醒,在处理响应期间,中间件的执行顺序是倒序执行的,这包括process_exception
。 如果异常中间件返回响应,那么中间件上面的中间件类的process_exception
方法根本就不会被调用。
process_template_response()
¶process_template_response
(request, response)¶request
是一个HttpRequest
对象。 response
是一个TemplateResponse
对象(或等价的对象),由Django视图或者中间件返回。
如果响应的实例有render()
方法,process_template_response()
在视图刚好执行完毕之后被调用,这表明了它是一个TemplateResponse
对象(或等价的对象)。
这个方法必须返回一个实现了render
方法的响应对象。 它可以修改给定的response.template_name
对象,通过修改 response
和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)
Django自动将视图或中间件引发的异常转换为具有错误状态代码的适当HTTP响应。 Certain exceptions转换为4xx状态码,而将未知异常转换为500状态码。
这种转换发生在每个中间件之前和之后(您可以将其视为每个洋葱层之间的薄膜),因此每个中间件总是可以依靠从调用其get_response
可调用。 中间件不需要担心在try/except
中调用get_response
,并处理由稍后的中间件或视图引发的异常。 Even
if the very next middleware in the chain raises an
Http404
exception, for example, your middleware won’t see
that exception; instead it will get an HttpResponse
object with a status_code
of 404.
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()
。
在大多数情况下,继承这种混合将足以使旧式中间件与新系统兼容,具有足够的向后兼容性。 新的短路语义对于现有的中间件将是无害的,甚至是有益的。 在少数情况下,中间件类可能需要一些更改才能适应新的语义。
这些是使用MIDDLEWARE
和MIDDLEWARE_CLASSES
之间的行为差异:
MIDDLEWARE_CLASSES
下,每个中间件总是会调用它的process_response
方法,即使早期的中间件通过从process_request
方法返回一个响应来短路。 在MIDDLEWARE
下,中间件的行为更像一个洋葱:一个响应在出路上经历的层是同样的层,可以看到请求的路径。 如果中间件发生短路,则只有中间件和MIDDLEWARE
之前的中间件才会看到响应。MIDDLEWARE_CLASSES
下,process_exception
适用于从中间件process_request
方法引发的异常。 在MIDDLEWARE
下,process_exception
仅适用于从TemplateResponse
的视图(或render
方法)引发的异常, 。 从中间件引发的异常转换为适当的HTTP响应,然后传递给下一个中间件。MIDDLEWARE_CLASSES
下,如果process_response
方法引发异常,则会跳过所有较早中间件的process_response
方法,并且500 内部 服务器 错误
HTTP响应总是返回(即使引发的异常例如是一个Http404
在MIDDLEWARE
下,从中间件引发的异常将立即转换为适当的HTTP响应,然后下一个中间件将会看到该响应。 由于中间件引发异常,中间件不会被跳过。2017年9月6日