Skip to main content

中间件

中间件是一个钩子到Django的请求/响应处理框架。它是一个轻,低级的“插件”系统,用于全局改变Django的输入或输出。

每个中间件组件负责做一些特定的功能。例如,Django包括一个中间件组件 AuthenticationMiddleware,它将用户与使用会话的请求相关联。

本文解释了中间件如何工作,如何激活中间件,以及如何编写自己的中间件。 Django附带一些内置的中间件,你可以使用开箱即用。它们记录在 内置中间件参考 中。

Changed in Django 1.10:

引入了一种新型中间件,用于新的 MIDDLEWARE 设置。如果您使用旧的 MIDDLEWARE_CLASSES 设置,则需要使用 适应老,定制中间件 才能使用新设置。本文档描述了新式中间件。有关旧样式中间件如何工作的说明,请参阅旧版本文档中的此页。

编写自己的中间件

中间件工厂是一个可调用,它接受 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(object):
    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 callable可能是实际视图(如果这是最后列出的中间件),或者它可能是链中的下一个中间件。当前的中间件不需要知道或关心它到底是什么,只是它代表了下一步。

上面是一个小小的简化 - 链中最后一个中间件的 get_response 可调用不会是实际的视图,而是来自处理程序的一个包装器方法,它处理应用 视图中间件,调用具有适当的URL参数的视图,以及应用 模板反应例外 中间件。

中间件可以在Python路径的任何地方生活。

__init__(get_response)

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

  • Django仅使用 get_response 参数初始化中间件,因此您不能将 __init__() 定义为需要任何其他参数。

  • 与每个请求一次调用的 __call__() 方法不同,当Web服务器启动时,__init__() 仅调用 once

Changed in Django 1.10:

在旧版本中,直到Web服务器响应其第一个请求才会调用 __init__()

在旧版本中,__init__() 不接受任何参数。要允许您的中间件在Django 1.9及更早版本中使用,请使 get_response 成为可选参数(get_response=None)。

将中间件标记为未使用

在启动时确定是否应该使用一个中间件有时是有用的。在这些情况下,您的中间件的 __init__() 方法可能会提高 MiddlewareNotUsed。 Django将从中间件进程中删除该中间件,并在 DEBUGTrue 时将调试消息记录到 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中间件类的排序的一些常见提示,请参见 中间件排序

中间件顺序和分层

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

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

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

其他中间件钩子

除了前面描述的基本请求/响应中间件模式,您可以向基于类的中间件添加三个其他特殊方法:

process_view()

process_view(request, view_func, view_args, view_kwargs)

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

在Django调用视图之前调用 process_view()

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

注解

在视图运行之前或在 process_view() 中访问中间件之中的 request.POST 将阻止在中间件之后运行的任何视图能够执行 修改请求的上传处理程序,并且通常应该避免。

CsrfViewMiddleware 类可以被认为是一个异常,因为它提供了 csrf_exempt()csrf_protect() 修饰器,允许视图在什么时候显式控制CSRF验证应该发生。

process_exception()

process_exception(request, exception)

requestHttpRequest 对象。 exception 是由视图函数引发的 Exception 对象。

当视图引发异常时,Django调用 process_exception()process_exception() 应返回 NoneHttpResponse 对象。如果它返回一个 HttpResponse 对象,模板响应和响应中间件将被应用,并将结果响应返回到浏览器。否则,默认异常处理 开始。

同样,中间件在响应阶段期间以相反的顺序运行,其包括 process_exception。如果异常中间件返回响应,那么该中间件上方的中间件类的 process_exception 方法将不会被调用。

process_template_response()

process_template_response(request, response)

requestHttpRequest 对象。 response 是由Django视图或中间件返回的 TemplateResponse 对象(或等效对象)。

在视图完成执行后,如果响应实例具有 render() 方法,则指示它是 TemplateResponse 或等效值,则调用 process_template_response()

它必须返回实现 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 应该被假定为太大而不能保持在存储器中。响应中间件可以将其包装在新的生成器中,但不能使用它。包装通常如下实现:

def wrap_streaming_content(content):
    for chunk in content:
        yield alter_content(chunk)

异常处理

Django自动将视图或中间件引发的异常转换为带有错误状态代码的适当HTTP响应。 某些例外 转换为4xx状态代码,而未知异常转换为500状态代码。

这种转换发生在每个中间件之前和之后(你可以把它看作是每一层洋葱之间的薄膜),这样每个中间件总是可以通过调用它的 get_response callable来获得一些HTTP响应。中间件不需要担心在 try/except 中打包他们对 get_response 的调用,并处理可能由后来的中间件或视图引发的异常。即使链中的下一个中间件引发了 Http404 异常,例如,您的中间件不会看到该异常;相反,它将获得 status_code 为404的 HttpResponse 对象。

升级Django 1.10之前的中间件

class django.utils.deprecation.MiddlewareMixin

Django提供 django.utils.deprecation.MiddlewareMixin 来简化创建与 MIDDLEWARE 和旧 MIDDLEWARE_CLASSES 兼容的中间件类。 Django包含的所有中间件类都与这两个设置兼容。

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

__call__() 方法:

  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_response 方法,即使较早的中间件通过从其 process_request 方法返回响应而短路。在 MIDDLEWARE 下,中间件的行为更类似于洋葱:响应在出口处经过的层是在进程中看到请求的同一层。如果中间件短路,只有中间件和它之前的层 MIDDLEWARE 将看到响应。

  2. MIDDLEWARE_CLASSES 下,process_exception 适用于从中间件 process_request 方法引发的异常。在 MIDDLEWARE 下,process_exception 仅适用于从视图(或从 TemplateResponserender 方法)引起的异常。从中间件引发的异常将转换为适当的HTTP响应,然后传递到下一个中间件。

  3. MIDDLEWARE_CLASSES 下,如果 process_response 方法引发异常,则跳过所有较早中间件的 process_response 方法,并且总是返回 500 Internal Server Error HTTP响应(即使所产生的异常例如是 Http404)。在 MIDDLEWARE 下,从中间件引发的异常将立即转换为适当的HTTP响应,然后下一个中间件将看到该响应。由于中间件引发异常,中间件从不跳过。