中间件

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

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

他的文档解释了中间件是如何工作的,如何激活中间件,以及如何编写自己的中间件。Django 具有一些内置的中间件,你可以直接使用。它们被记录在 built-in middleware reference 中。

编写自己的中间件

中间件工厂是一个可调用的程序,它接受 get_response 可调用并返回中间件。中间件是可调用的,它接受请求并返回响应,就像视图一样。

中间件可以被写成这样的函数:

  1. def simple_middleware(get_response):
  2. # One-time configuration and initialization.
  3. def middleware(request):
  4. # Code to be executed for each request before
  5. # the view (and later middleware) are called.
  6. response = get_response(request)
  7. # Code to be executed for each request/response after
  8. # the view is called.
  9. return response
  10. return middleware

或者它可以写成一个类,它的实例是可调用的,如下:

  1. class SimpleMiddleware:
  2. def __init__(self, get_response):
  3. self.get_response = get_response
  4. # One-time configuration and initialization.
  5. def __call__(self, request):
  6. # Code to be executed for each request before
  7. # the view (and later middleware) are called.
  8. response = self.get_response(request)
  9. # Code to be executed for each request/response after
  10. # the view is called.
  11. return response

Django 提供的 get_response 响应可能是实际视图(如果这是最后列出的中间件),或者它可能是链中的下一个中间件。不需要知道或关心当前的中间件到底是什么,它只是代表了下一步的内容。

以上是一个轻微的简化——链中最后一个中间件调用的 get_response 可不是实际视图,而是处理程序的包装方法,它负责应用 view middleware,调用具有适当URL参数的视图,并应用 template-responseexception 中间件。

Middleware can either support only synchronous Python (the default), only asynchronous Python, or both. See 异步支持 for details of how to advertise what you support, and know what kind of request you are getting.

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

__init__(get_response)

中间件工厂必须接受 get_response 参数。还可以初始化中间件的一些全局状态。记住两个注意事项:

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

标记未使用的中间件

在启动时确定是否应该使用一个中间件有时是有用的。在这些情况下,您的中间件的 __init__() 方法可能会引发 MiddlewareNotUsed。Django 将从中间件进程中删除该中间件,并将调试消息记录到 django.request 日志:设置 DEBUGTrue

激活中间件

若要激活中间件组件,请将其添加到 Django 设置中的 MIDDLEWARE 列表中。

MIDDLEWARE 中,每个中间件组件由字符串表示:指向中间件工厂的类或函数名的完整 Python 路径。例如,这里创建的默认值是 django-admin startproject

  1. MIDDLEWARE = [
  2. 'django.middleware.security.SecurityMiddleware',
  3. 'django.contrib.sessions.middleware.SessionMiddleware',
  4. 'django.middleware.common.CommonMiddleware',
  5. 'django.middleware.csrf.CsrfViewMiddleware',
  6. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  7. 'django.contrib.messages.middleware.MessageMiddleware',
  8. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  9. ]

Django 安装不需要任何中间件——如果您愿意的话,MIDDLEWARE 可以为空——但是强烈建议您至少使用 CommonMiddleware

MIDDLEWARE 的顺序很重要,因为中间件会依赖其他中间件。例如:类 AuthenticationMiddleware 在会话中存储经过身份验证的用户;因此,它必须在 SessionMiddleware 后面运行 。中间件。Session中间件。请参阅 Middleware ordering ,用于一些关于 Django 中间件类排序的常见提示。

中间件顺序与分层

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

你可以把它想象成一个洋葱:每个中间件类都是一个“层”,它覆盖了洋葱的核心。如果请求通过洋葱的所有层(每一个调用 get_response )以将请求传递到下一层,一直到内核的视图,那么响应将在返回的过程中通过每个层(以相反的顺序)。

如果其中一层决定停止并返回响应而不调用get_response,那么该层(包括视图)中的洋葱层都不会看到请求或响应。响应将只通过请求传入的相同层返回。

其他中间件钩子

除前面说书的基础请求/响应中间件模式外,你可以给基于类的中间件添加三种其他特殊方法:

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 调用视图前被调用。

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

注解

在视图运行前或在 process_view() 内访问中间件里的 request.POST 将阻止中间件之后运行的任何视图修改请求的上传处理程序 (modify the upload handlers for the request ),通常应该避免这样。

CsrfViewMiddleware 类可以被视为一个例外,因为它提供 csrf_exempt()csrf_protect() 装饰器,它们允许视图完全控制 CSRF 验证在什么时候进行。

process_exception()

process_exception(request, exception)

request 是一个 HttpRequest 对象。 exception 是一个由视图函数引发的 Exception 对象。

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

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

process_template_response()

process_template_response(request, response)

request 是一个 HttpRequest 对象。responseTemplateResponse 对象(或者等效对象),它通过 Django 视图或中间件返回。

process_template_response() 在视图被完全执行后调用,如果响应实例有 render() 方法,表明它是一个 TemplateResponse 或等效对象。

它必须返回一个实现了 render 方法的响应对象。它可以通过改变``response.template_name`` 和 response.context_data 来改变给定的 response ,或者它可以创建和返回全新的 TemplateResponse 或等效对象。

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

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

处理流式响应

HttpResponse 不同,StreamingHttpResponse 没有 content 属性。因此,中间件不能再假设所有响应有 content 属性。如果它们需要访问 content,它们必须为流式响应进行测试,并且调整其行为:

  1. if response.streaming:
  2. response.streaming_content = wrap_streaming_content(response.streaming_content)
  3. else:
  4. response.content = alter_content(response.content)

注解

streaming_content 被假设为体积太大而无法在内存中保存。响应中间件可以将其包装在一个新的生成器里,但不能使用它。包装通常如下实现:

  1. def wrap_streaming_content(content):
  2. for chunk in content:
  3. yield alter_content(chunk)

异常处理

Django 自动转换视图引发的异常,或者带有错误状态代码的特定 HTTP 响应内的中间件引发的异常。某些异常( Certain exceptions )被转换为 4xx 状态代码,而未知异常被转换为 500 状态代码。

这个变换发生在每个中间件的前后(你可以把它想象成洋葱每层中间的薄膜),因此每个中间件总是可以依赖于从调用它的 get_response 回调中获得某种类型的HTTP响应。中间件不需要担心它们对 get_response 的调用包装在 try/except 里,也不需要担心处理稍后的中间件或视图引发的异常。即使链中的下一个中间件引发了 Http404 异常,比如中间件不会查看异常,相反,它会得到一个带有404 status_codeHttpResponse 对象。

异步支持

New in Django 3.1.

Middleware can support any combination of synchronous and asynchronous requests. Django will adapt requests to fit the middleware’s requirements if it cannot support both, but at a performance penalty.

By default, Django assumes that your middleware is capable of handling only synchronous requests. To change these assumptions, set the following attributes on your middleware factory function or class:

  • sync_capable is a boolean indicating if the middleware can handle synchronous requests. Defaults to True.
  • async_capable is a boolean indicating if the middleware can handle asynchronous requests. Defaults to False.

If your middleware has both sync_capable = True and async_capable = True, then Django will pass it the request without converting it. In this case, you can work out if your middleware will receive async requests by checking if the get_response object you are passed is a coroutine function, using asyncio.iscoroutinefunction().

The django.utils.decorators module contains sync_only_middleware(), async_only_middleware(), and sync_and_async_middleware() decorators that allow you to apply these flags to middleware factory functions.

The returned callable must match the sync or async nature of the get_response method. If you have an asynchronous get_response, you must return a coroutine function (async def).

process_view, process_template_response and process_exception methods, if they are provided, should also be adapted to match the sync/async mode. However, Django will individually adapt them as required if you do not, at an additional performance penalty.

Here’s an example of how to create a middleware function that supports both:

  1. import asyncio
  2. from django.utils.decorators import sync_and_async_middleware
  3. @sync_and_async_middleware
  4. def simple_middleware(get_response):
  5. # One-time configuration and initialization goes here.
  6. if asyncio.iscoroutinefunction(get_response):
  7. async def middleware(request):
  8. # Do something here!
  9. response = await get_response(request)
  10. return response
  11. else:
  12. def middleware(request):
  13. # Do something here!
  14. response = get_response(request)
  15. return response
  16. return middleware

注解

If you declare a hybrid middleware that supports both synchronous and asynchronous calls, the kind of call you get may not match the underlying view. Django will optimize the middleware call stack to have as few sync/async transitions as possible.

Thus, even if you are wrapping an async view, you may be called in sync mode if there is other, synchronous middleware between you and the view.

升级 Django 1.10 之前的中间件

class django.utils.deprecation.``MiddlewareMixin

Django provides django.utils.deprecation.MiddlewareMixin to ease creating middleware classes that are compatible with both MIDDLEWARE and the old MIDDLEWARE_CLASSES, and support synchronous and asynchronous requests. All middleware classes included with Django are compatible with both settings.

The mixin provides an __init__() method that requires a get_response argument and stores it in 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_response 方法返回响应而短路。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 响应,然后下一个中间件将看到响应。中间件不会因为中间件引发异常而被跳过。

Changed in Django 3.1:

Support for asynchronous requests was added to the MiddlewareMixin.