原文:Middleware
中间件是Django的请求/响应处理中的钩子框架。它是一个用于全局修改Django输入或输出的轻量、低级的“插件”系统。
每个中间件组件负责一些特定的功能。例如,Django包含一个中间件组件, AuthenticationMiddleware
,它使用会话关联请求和用户。
本文档介绍了中间件是如何工作的,如何激活中间件以及如何编写自己的中间件。Django自带一些内置的中间件,你可以开箱即用。它们记录在内置中间件参考中。
激活中间件¶
要激活一个中间件,将其添加到你的Django设置的MIDDLEWARE_CLASSES
列表中。
在MIDDLEWARE_CLASSES
中,中间件组件用字符串表示:中间件类名的完整Python路径。例如,下面是由django-admin startproject
命令创建的默认值:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Django的安装不需要任何中间件,如果你喜欢的话,MIDDLEWARE_CLASSES
可以是空的,但是,强烈建议你至少使用CommonMiddleware
。
MIDDLEWARE_CLASSES
中的顺序很重要,因为一个中间件可能依赖于其他中间件。例如,AuthenticationMiddleware
存储会话中的认证用户;因此,它必须在SessionMiddleware
之后运行。见中间件顺序以获得有关Django中间件类排序的一些常用提示。
钩子和应用程序顺序¶
在请求阶段,调用视图之前,Django根据它在MIDDLEWARE_CLASSES
中定义的顺序自上而下应用中间件。两个可用的钩子:
在响应阶段,调用视图后,中间件都以相反的顺序,从下往上被应用。三个可供选择的钩子:
process_exception()
(只有当视图引发了一个异常的时候)process_template_response()
(仅用于模板响应)process_response()
如果你愿意,你也可以把它想像成一个洋葱:每个中间件是一个“层”,它包裹着视图。
每个钩子的行为在下面进行说明。
编写你自己的中间件¶
编写你自己的中间件是容易的。每个中间件组件是单个Python类,它定义了下面一个或多个方法:
process_request()
¶
process_request
(request)¶
request
是一个HttpRequest
对象。
在Django决定要执行哪个视图时,在每个请求上调用process_request()
。
它应该要么返回None
,要么返回一个HttpResponse
对象。如果返回None
,Django将继续处理这个请求,执行其他process_request()
中间件,然后是process_view()
中间件,最后是相应的视图。如果返回HttpResponse
对象,Django将不会调用任何其他请求,视图或异常中间件,或者相应的视图;它将应用响应中间件到那个HttpResponse
上,然后返回结果。
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_request
或者process_view
的中间件中访问request.POST
会阻止在中间件之后运行的任何视图为请求修改上传处理器的能力,并且通常应该避免。
CsrfViewMiddleware
类可以被认为是一个例外,因为它提供了csrf_exempt()
和csrf_protect()
装饰器,它们允许视图显式的控制CSRF验证应该在什么情况下发生。
process_template_response()
¶
process_template_response
(request, response)¶
request
是一个HttpRequest
对象。response
是由一个Django视图或一个中间件返回的TemplateResponse
对象(或等效对象)。
在视图结束执行后,process_template_response()
就会被调用,如果该response实例有一个render()
方法,这表明它是一个TemplateResponse
或等效对象。
它必须返回一个实现了render
方法的response对象。通过修改response.template_name
和response.context_data
,它可能会改变给定的response
,或者它也可能创建并返回一个全新的TemplateResponse
或等效对象。
你并不需要显式地渲染响应 - 一旦所有的模板响应中间件被调用,反应就会被自动渲染。
中间件以相反的顺序在响应阶段运行,其中包括process_template_response()
。
process_response()
¶
process_response
(request, response)¶
request
是一个HttpRequest
对象。response
是由一个Django视图或一个中间件返回的HttpResponse
或StreamingHttpResponse
对象。
在将所有的响应返回给浏览器之前,会为它们调用process_response()
。
它必须返回一个HttpResponse
或StreamingHttpResponse
对象。它可能会改变给定的response
,或者它也可能创建并返回一个全新的HttpResponse
或StreamingHttpResponse
。
不像process_request()
和process_view()
方法,process_response()
方法总是被调用,甚至是相同中间件类的process_request()
和process_view()
方法被跳过时也会被调用(因为较早的中间件方法返回一个HttpResponse
)。特别是,这意味着,你的process_response()
方法不能依赖于process_request()
中完成的步骤。
最后,请记住,在响应结点,中间件是以相反的顺序,自下而上应用的。这意味着,在MIDDLEWARE_CLASSES
之后定义的类将会首先运行。
处理流媒体响应¶
不像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)
process_exception()
¶
process_exception
(request, exception)¶
request
是一个HttpRequest
对象。exception
是一个由视图函数抛出的Exception
对象。
当一个视图抛出异常时,Django调用process_exception()
。process_exception()
应该返回None
或HttpResponse
对象。如果返回HttpResponse
对象,那么模板响应和响应中间件将被应用,然后会将所产生的响应返回给浏览器。否则,默认的异常处理介入。
再次,在响应阶段,中间件以相反的顺序运行,其中包括process_exception
。如果一个异常中间件返还一个响应,那么在该中间件之上的中间件类将完全不会被调用。
__init__()
¶
因为中间件类只是用来在process_*
方法中占位的,所以大多数中间件类并不需要一个初始化器。如果你确实需要一些全局的状态,那么你可以使用__init__
建立初始化器。但是,请记住以下两点:
- Django不带任何参数对你的中间件初始化,所以你不能定义需要参数的
__init__
。 - 不像
process_*
方法那样每个请求都会调用一次,__init__
只有当web服务器第一次响应时被调用一次。
将中间件标记为未使用¶
在运行时间确定是否应使用一个中间件有时是有用的。在这些情况下,你的中间件的__init__
方法可能引发django.core.exceptions.MiddlewareNotUsed
。那么Django会从中间件处理过程中删除该中间件,并且当DEBUG
被设置为True
时,调试信息将被记录到django.request
记录器中。
Django 1.8的改动:
之前,
MiddlewareNotUsed
异常不被记录。
指南¶
- 中间件类无需从任何类继承。
- 中间件类可以存放在Python路径的任何位置。Django所关心的是,
MIDDLEWARE_CLASSES
设置包含其路径。 - 自由查看Django可用的中间件中的例子。
- 如果你编写一个你觉得这是队别人有用的中间件组件,那么把它贡献给社区吧!让我们知道,我们会考虑将它加入Django中。