中间件¶ T0>

中间件是一个钩入Django请求/响应处理的框架。 这是一个轻松的,低级别的“插件”系统,用于在全球范围内改变Django的输入或输出。

每个中间件组件都负责做一些特定的功能。 For example, Django includes a middleware component, AuthenticationMiddleware, that associates users with requests using sessions.

本文档介绍了中间件的工作原理,如何激活中间件以及如何编写自己的中间件。 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可调用可能是实际的视图(如果这是最后列出的中间件)或者它可能是链中的下一个中间件。 目前的中间件不需要知道或关心究竟是什么,只是它表示接下来的任何事情。

The above is a slight simplification – the get_response callable for the last middleware in the chain won’t be the actual view but rather a wrapper method from the handler which takes care of applying view middleware, calling the view with appropriate URL arguments, and applying template-response and exception middleware.

中间件可以在你的Python路径上的任何地方居住。

__init__(get_response)

中间件工厂必须接受一个get_response参数。 您也可以初始化中间件的一些全局状态。 请记住几个警告:

  • Django仅使用get_response参数初始化您的中间件,因此您无法将__init__()定义为需要其他任何参数。
  • 与每个请求调用一次的__call__()方法不同,当Web服务器启动时,__init__()仅调用一次

将中间件标记为未使用

在启动时确定是否应该使用一个中间件是有用的。 在这些情况下,您的中间件的__init__()方法可能会引发MiddlewareNotUsed Django will then remove that middleware from the middleware process and log a debug message to the django.request logger when DEBUG is 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',
]

A Django installation doesn’t require any middleware — MIDDLEWARE can be empty, if you’d like — but it’s strongly suggested that you at least use CommonMiddleware.

MIDDLEWARE中的顺序很重要,因为中间件可以依赖于其他中间件。 For instance, AuthenticationMiddleware stores the authenticated user in the session; therefore, it must run after SessionMiddleware. 有关Django中间件类的排序的一些常见提示,请参阅Middleware ordering

中间件的顺序和分层

在请求阶段,在调用视图之前,Django按照在MIDDLEWARE中定义的顺序从上到下应用中间件。

你可以把它想象成一个洋葱:每个中间件类都是一个包装视图的“图层”,这是洋葱的核心。 如果请求经过洋葱的所有层(每一个调用get_response将请求传递到下一层),一直到核心的视图,响应将通过每一层(以相反的顺序)在退出的路上。

如果其中一个层决定短路并返回一个响应,而没有调用它的get_response,则该层内的洋葱层(包括视图)都不会看到请求或响应。 响应只会通过与传入的请求相同的层返回。

其他中间件钩子

除了前面介绍的基本请求/响应中间件模式外,还可以将其他三种特殊方法添加到基于类的中间件中:

process_view()

process_viewrequestview_funcview_argsview_kwargs/ T5>

request is an HttpRequest object. view_func是Django即将使用的Python函数。 (这是实际的函数对象,而不是函数的名称作为字符串。) view_args是将被传递给视图的位置参数的列表,view_kwargs是将被传递给视图的关键字参数的字典。 view_argsview_kwargs都不包含第一个视图参数(request)。

process_view() is called just before Django calls the view.

它应该返回NoneHttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他的process_view()中间件,然后执行相应的视图。 如果它返回一个HttpResponse对象,Django不会打扰调用适当的视图。它会将响应中间件应用于HttpResponse并返回结果。

注意

Accessing request.POST inside middleware before the view runs or in process_view() will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided.

因为它提供了csrf_exempt()csrf_protect()装饰器,所以可以认为CsrfViewMiddleware类是异常的,它允许视图明确地控制指出应该发生CSRF验证。

process_exception()

process_exception请求异常

request is an HttpRequest object. exception is an Exception object raised by the view function.

Django calls process_exception() when a view raises an exception. process_exception() should return either None or an HttpResponse object. 如果它返回一个HttpResponse对象,模板响应和响应中间件将被应用并且返回给浏览器。 否则,default exception handling开始。

在响应阶段,中间件再次以相反的顺序运行,其中包括process_exception 如果一个异常中间件返回一个响应,则该中间件上面的中间件类的process_exception方法根本不会被调用。

process_template_response()

process_template_response tt>(requestresponse

request is an HttpRequest object. response is the TemplateResponse object (or equivalent) returned by a Django view or by a middleware.

process_template_response() is called just after the view has finished executing, if the response instance has a render() method, indicating that it is a TemplateResponse or equivalent.

它必须返回一个实现render方法的响应对象。 它可以通过改变response.template_nameresponse.context_data来改变给定的response,也可以创建并返回一个全新的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 should be assumed to be too large to hold in memory. 响应中间件可能将其包装在一个新的生成器中,但不能使用它。 包装通常实施如下:

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.

升级pre-Django 1.10风格的中间件

django.utils.deprecation。 MiddlewareMixin T0> ¶ T1>

Django provides django.utils.deprecation.MiddlewareMixin to ease creating middleware classes that are compatible with both MIDDLEWARE and the old MIDDLEWARE_CLASSES. Django中包含的所有中间件类都与这两个设置兼容。

mixin提供了一个__init__()方法,它接受一个可选的get_response参数并将其存储在self.get_response中。

The __call__() method:

  1. 调用self.process_request(request)(如果已定义)。
  2. 调用self.get_response(request)来获得来自后来的中间件和视图的响应。
  3. 调用self.process_response(request, response)(如果已定义)。
  4. 返回响应。

如果与MIDDLEWARE_CLASSES一起使用,则不会使用__call__()方法。 Django直接调用process_request()process_response()

在大多数情况下,从这个mixin继承将足以使旧式中间件与新系统兼容,并具有足够的向后兼容性。 新的短路语义对现有的中间件来说是无害的,甚至是有益的。 在少数情况下,中间件类可能需要进行一些更改以适应新的语义。

这些是使用MIDDLEWAREMIDDLEWARE_CLASSES之间的行为差​​异:

  1. MIDDLEWARE_CLASSES下,即使先前的中间件通过返回来自其process_request方法的响应而短路,每个中间件也将始终调用其process_response方法。 MIDDLEWARE下,中间件的行为更像是一个洋葱:一个响应在出路上经过的层是在看到请求的同一层。 If a middleware short-circuits, only that middleware and the ones before it in MIDDLEWARE will see the response.
  2. MIDDLEWARE_CLASSES下,process_exception应用于从中间件process_request方法引发的异常。 Under MIDDLEWARE, process_exception applies only to exceptions raised from the view (or from the render method of a TemplateResponse). 从中间件引发的异常转换为适当的HTTP响应,然后传递到下一个中​​间件。
  3. Under MIDDLEWARE_CLASSES, if a process_response method raises an exception, the process_response methods of all earlier middleware are skipped and a 500 Internal Server Error HTTP response is always returned (even if the exception raised was e.g. an Http404). MIDDLEWARE下,从中间件引发的异常将立即转换为适当的HTTP响应,然后下一个中间件在行中将看到该响应。 由于中间件引发异常,中间件不会被跳过。