本篇译自 django 官方文档:https://docs.djangoproject.com/en/1.6/topics/http/middleware/
中间件是嵌入 django 的 request/response 处理过程的一套钩子框架。它是一个轻量级的底层嵌入系统,可以对 django 的输入输出做整体的修改。
激活中间件
将需要激活的中间件添加到项目 settings 文件内即可,初始状态下他是这个样子的:
MIDDLEWARE_CLASSES = ( ' 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 ' , )
MIDDLEWARE_CLASS 的元素是有顺序的,因为中间件之间可能存在依赖关系。
钩子和应用顺序
中间件具体是由包装了一些特定方法(钩子)的类来实现的。
在 request 阶段,调用 view 之前,django 依 MIDDLEWARE_CLASSES 定义的顺序依次套用各个中间件,但其中只有两个钩子会被调用:
- process_request()
- process_view()
在 response 阶段,调用 view 之后,django 以 request 阶段的倒序来套用各个中间件,其中会被调用的有三种钩子:
- process_exception() (只有 view 引发异常的时候)
- process_template_response (仅限 template response)
- process_response()
整个应用过程可以看做是包裹着 view 内核的一层层洋葱皮,每个中间件就是一层。
process_request(request)
本方法会对每一个 request 调用,在 django 决定使用哪个 view 之前。
本方法只能返回两种值:None 或者一个 HttpResponse 对象。当返回 None 的时候,django 会继续套用其他中间件和 view;当返回 HttpResponse 对象的时候,django 会直接跳过其他 request 阶段的中间件、process_exception() 和 view,仅调用 response 阶段中间件然后返回。
process_view(request,view_func,view_args,view_kwargs)
本方法调用于 view 之前。(view_func 参数应当是一个可调用对象,而不是函数名的字符串。另,后面的参数中都不包含 request 对象)
返回值同上
注意:在 process_request() 和 process_view() 中不应当访问 request.POST 和 request.REQUEST
process_template_response(request,response)
本方法调用于 view 刚刚完成之后。response 参数应当是一个由 view 或中间件返回的 TemplateResponse 对象(或等价对象),这一点可以通过 response 实例中是否含有 .render() 方法鉴别。
本方法必须返回一个实现了 render() 方法的 response 对象。本方法可以通过修改 response.template_name 和 response.context_data 属性来修改原本的 response 并返回,也可以 自建一个新的 TemplateResponse 对象(或等价对象)。
你不需要显式地去渲染 response,这个过程会在本方法调用的最后被自动执行。
process_response(request,response)
本方法调用于每一个 response 被返回给客户端之前。其中 response 参数是一个由 view 或中间件返回的 HttpResponse 对象,或 StreamingHttpResponse 对象。
本方法也必然的只能返回这两种对象。被返回的对象同样可以是从参数 response 修改而来或自建的新对象。
和 process_request() , process_view() 不同,本方法总是会被调用,即使他们处于同一中间件内。前面提到过,request 阶段中间件如果返回了 response 对象,那么之后的 request 阶段中间件会被跳过,但这实际指的是中间件里的那几种方法,对于本方法,还是要调用的。这就意味着,process_respnse() 的处理过程不应依赖于 process_request() 的结果,因为他们是可能被跳过的,但本方法不会。
处理 StreamingHttpResponse
更新于 django 1.5
response 也可以是一个 StreamingHttpResponse。
不像 HttpResponse,StreamingHttpResponse 并没有 content 属性。因此,中间件不可以再默认 response 对象总是有 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(request,exception)
当 view 抛出异常时,本方法就会被调用。本方法应当返回 None 或一个 HttpResponse 对象,如果返回 response,那么此方法所在中间件外层的 response 阶段中间件将继续被调用;如果返回 None,则由默认异常处理机制来接手。
这个钩子可以用来发送错误通知,写入日志文件,甚至恢复错误。
__init__()
一般中间件并不需要初始化数据,如果确实需要,则可以定义在此方法内。不过需要注意:
- django 初始化中间件时并不会传入多余的参数,所以本方法只接受一个 self
- 不像 process_* 这些方法会对每一个 request 调用,本方法只会在服务器第一次响应时进行
因此,本方法更多是作为一种自检机制在使用。因为中间件框架提供了一种运行时停用某个中间件的方法,就是引发一个 django.core.exceptions.MiddlewareNotUsed 异常。那么 __init__() 方法就可以利用这一点在服务开始时进行自检。