Skip to content

Latest commit

 

History

History
184 lines (99 loc) · 16.1 KB

中间件.md

File metadata and controls

184 lines (99 loc) · 16.1 KB

原文: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中定义的顺序自上而下应用中间件。两个可用的钩子:

在响应阶段,调用视图后,中间件都以相反的顺序,从下往上被应用。三个可供选择的钩子:

如果你愿意,你也可以把它想像成一个洋葱:每个中间件是一个“层”,它包裹着视图。

每个钩子的行为在下面进行说明。

编写你自己的中间件

编写你自己的中间件是容易的。每个中间件组件是单个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_argsview_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_nameresponse.context_data,它可能会改变给定的response,或者它也可能创建并返回一个全新的TemplateResponse或等效对象。

你并不需要显式地渲染响应 - 一旦所有的模板响应中间件被调用,反应就会被自动渲染。

中间件以相反的顺序在响应阶段运行,其中包括process_template_response()

process_response()

process_response(request, response)

request是一个HttpRequest对象。response是由一个Django视图或一个中间件返回的HttpResponseStreamingHttpResponse对象。

在将所有的响应返回给浏览器之前,会为它们调用process_response()

它必须返回一个HttpResponseStreamingHttpResponse对象。它可能会改变给定的response,或者它也可能创建并返回一个全新的HttpResponseStreamingHttpResponse

不像process_request()process_view()方法,process_response()方法总是被调用,甚至是相同中间件类的process_request()process_view()方法被跳过时也会被调用(因为较早的中间件方法返回一个HttpResponse)。特别是,这意味着,你的process_response()方法不能依赖于process_request()中完成的步骤。

最后,请记住,在响应结点,中间件是以相反的顺序,自下而上应用的。这意味着,在MIDDLEWARE_CLASSES之后定义的类将会首先运行。

处理流媒体响应

不像HttpResponseStreamingHttpResponse并没有一个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()应该返回NoneHttpResponse对象。如果返回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中。