# Django 3.2.11.dev 文档

# 中间件

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

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

他的文档解释了中间件是如何工作的，如何激活中间件，以及如何编写自己的中间件。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` 响应可能是实际视图（如果这是最后列出的中间件），或者它可能是链中的下一个中间件。不需要知道或关心当前的中间件到底是什么，它只是代表了下一步的内容。

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

中间件可以只支持同步Python(默认)，或异步Python，或者二者都支持。查看 异步支持 来了解如何通知你支持的是什么以及如何知道你是哪种请求。

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

### `__init__(get_response)`

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

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

### 标记未使用的中间件

在启动时确定是否应该使用一个中间件有时是有用的。在这些情况下，您的中间件的 `__init__()` 方法可能会引发 `MiddlewareNotUsed`。Django 将从中间件进程中删除该中间件，并将调试消息记录到 django.request 日志：设置 `DEBUG` 为 `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',
    ]

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

`MIDDLEWARE` 的顺序很重要，因为中间件会依赖其他中间件。例如：类 `AuthenticationMiddleware` 在会话中存储经过身份验证的用户；因此，它必须在 `SessionMiddleware` 后面运行 。中间件。Session中间件。请参阅 中间件顺序 ，用于一些关于 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_args` 和 `view_kwargs` 都不包含第一个视图参数 ( `request` )。

`process_view()` 只在 Django 调用视图前被调用。

它应该返回 `None` 或 `HttpResponse` 对象。如果它返回 `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()` 应该返回 `None` 或 `HttpResponse` 对象。如果它返回一个 `HttpResponse` 对象，模板响应和响应中间件将被应用且会将结果响应返回浏览器。否则，就会开始默认异常处理（ default exception handling ）。

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

### `process_template_response()`

`process_template_response`(request, response)

`request` 是一个 `HttpRequest` 对象。`response` 是 `TemplateResponse` 对象（或者等效对象），它通过 Django 视图或中间件返回。

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

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

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

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

## 处理流式响应

与 `HttpResponse` 不同，`StreamingHttpResponse` 没有 `content` 属性。因此，中间件不能再假设所有响应有 `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 响应内的中间件引发的异常。某些异常（ Certain exceptions ）被转换为 4xx 状态代码，而未知异常被转换为 500 状态代码。

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

You can set `DEBUG_PROPAGATE_EXCEPTIONS` to `True` to skip this conversion and propagate exceptions upwards.

## 异步支持

New in Django 3.1.

中间件支持同步和异步请求的任意组合。如果Django不能同时支持它们，它会调整请求来适应中间件的需求，但会有性能损失。

默认情况下，Django假设你的中间件只能处理同步请求。如果要改变这种模式，需要在你的中间件工厂函数或类中添加入如下属性：

- `sync_capable` 是一个布尔值，来表明中间件是否处理同步请求。默认为 `True`。
- `async_capable` 是一个布尔值，来表明中间件是否处理异步请求。默认为 `False`。

如果中间件的``sync_capable = True`` 并且 `async_capable = True`，那么Django 会将请求传递给它而不进行转换。在这种情况下，你可以使用 `asyncio.iscoroutinefunction()` 来检查传递的 `get_response` 是否是一个协同函数，从而确定中间件是否接收到异步请求。

`django.utils.decorators` 模块包含 `sync_only_middleware()`，`async_only_middleware()` 和 `sync_and_async_middleware()` 装饰器，允许你将这些标志应用到中间件工厂函数中。

返回的可调用对象必须符合 `get_response` 方法的同步或异步性质。如果你有一个异步的 `get_response`，你必须返回一个协程函数（`async def`）。

`process_view`、`process_template_response` 和 `process_exception` 方法，如果有的话，也应该进行调整以匹配同步／异步模式。然而，如果你不这样做，Django 会根据需要单独调整它们，但会有额外的性能损失。

下面以一个例子来说明如何创建一个支持这两种功能的中间件函数：

    import asyncio
    from django.utils.decorators import sync_and_async_middleware

    @sync_and_async_middleware
    def simple_middleware(get_response):
        # One-time configuration and initialization goes here.
        if asyncio.iscoroutinefunction(get_response):
            async def middleware(request):
                # Do something here!
                response = await get_response(request)
                return response

        else:
            def middleware(request):
                # Do something here!
                response = get_response(request)
                return response

        return middleware

注解

如果你声明了一个同时支持同步和异步调用的混合中间件，你得到的调用种类可能与底层视图不匹配。Django 会优化中间件调用栈，使其尽可能少的同步／异步转换。

因此，即使你包装的是一个异步视图，如果在你和视图之间有其他的、同步的中间件，你也可能会在同步模式下被调用。

## 升级 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 中继承就足以使一个旧式中间件与新系统兼容，并具有足够的向后兼容性。新的短路语义对现有中间件无害甚至有益。在少数情况下，中间件类可能需要一些改变来适应新的语义。

`MIDDLEWARE` 和 `MIDDLEWARE_CLASSES` 在使用上有些行为差异：

1. `MIDDLEWARE_CLASSES` 下，每个中间件将始终调用它的 `process_response` 方法，即使早期的中间件通过从其 `process_response` 方法返回响应而短路。`MIDDLEWARE` 下，中间件行为更像洋葱：响应在输出时经过的层与在输入时看到请求的层相同。如果一个中间件短路，只有那个中间件和之前的中间件可以看到响应。
2. 在 `MIDDLEWARE_CLASSES` 下，`process_exception` 应用于中间件 `process_request` 方法引发的异常。在 `MIDDLEWARE` 下，`process_exception` 只应用于视图引发的异常（或者从 `TemplateResponse` 的 `render` 方法引发的异常）。中间件引发的异常被转换为合适的 HTTP 响应，然后传递到下一个中间件。
3. `MIDDLEWARE_CLASSES` 下，如果 `process_response` 方法引发了异常，所有更早之前的中间件的 `process_response` 方法会被跳过，并一直返回 `500 Internal Server Error` 的 HTTP 响应（即使引发的异常是例如 `Http404` ）。在 `MIDDLEWARE` ，一个中间件引发的异常将立刻被转换为合适的 HTTP 响应，然后下一个中间件将看到响应。中间件不会因为中间件引发异常而被跳过。

Changed in Django 3.1:

`MiddlewareMixin` 增加了对异步请求的支持。