---
title: "中间件"
version: 4.2
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/4.2/topics/http/middleware/
canonical: https://djangodocs.dev/zh-hans/4.2/topics/http/middleware/
---
# 中间件

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

每个中间件组件负责做一些特定的功能。例如，Django 包含一个中间件组件 [`AuthenticationMiddleware`](/zh-hans/4.2/ref/middleware/#django.contrib.auth.middleware.AuthenticationMiddleware)，它使用会话将用户与请求关联起来。

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

## 编写自己的中间件

中间件工厂是一个可调用的程序，它接受 `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](#view-middleware)，调用具有适当URL参数的视图，并应用 [template-response](#template-response-middleware) 和 [exception](#exception-middleware) 中间件。

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

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

### `__init__(get_response)`

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

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

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

在启动时确定是否应该使用一个中间件有时是有用的。在这些情况下，您的中间件的 `__init__()` 方法可能会引发 [`MiddlewareNotUsed`](/zh-hans/4.2/ref/exceptions/#django.core.exceptions.MiddlewareNotUsed)。Django 将从中间件进程中删除该中间件，并将调试消息记录到 [django.request](/zh-hans/4.2/ref/logging/#django-request-logger) 日志：设置 [`DEBUG`](/zh-hans/4.2/ref/settings/#std-setting-DEBUG) 为 `True`。

## 激活中间件

若要激活中间件组件，请将其添加到 Django 设置中的 [`MIDDLEWARE`](/zh-hans/4.2/ref/settings/#std-setting-MIDDLEWARE) 列表中。

在 [`MIDDLEWARE`](/zh-hans/4.2/ref/settings/#std-setting-MIDDLEWARE) 中，每个中间件组件由字符串表示：指向中间件工厂的类或函数名的完整 Python 路径。例如，这里创建的默认值是 [`django-admin startproject`](/zh-hans/4.2/ref/django-admin/#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`](/zh-hans/4.2/ref/settings/#std-setting-MIDDLEWARE) 可以为空——但是强烈建议您至少使用 [`CommonMiddleware`](/zh-hans/4.2/ref/middleware/#django.middleware.common.CommonMiddleware)。

[`MIDDLEWARE`](/zh-hans/4.2/ref/settings/#std-setting-MIDDLEWARE) 的顺序很重要，因为中间件会依赖其他中间件。例如：类 [`AuthenticationMiddleware`](/zh-hans/4.2/ref/middleware/#django.contrib.auth.middleware.AuthenticationMiddleware) 在会话中存储经过身份验证的用户；因此，它必须在 [`SessionMiddleware`](/zh-hans/4.2/ref/middleware/#django.contrib.sessions.middleware.SessionMiddleware) 后面运行 。中间件。Session中间件。请参阅 [中间件顺序](/zh-hans/4.2/ref/middleware/#middleware-ordering) ，用于一些关于 Django 中间件类排序的常见提示。

## 中间件顺序与分层

在请求阶段，在调用视图之前，Django 按照定义的顺序应用中间件 [`MIDDLEWARE`](/zh-hans/4.2/ref/settings/#std-setting-MIDDLEWARE)，自顶向下。

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

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

## 其他中间件钩子

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

### `process_view()`

#### `process_view(request, view_func, view_args, view_kwargs)`

`request` 是一个 [`HttpRequest`](/zh-hans/4.2/ref/request-response/#django.http.HttpRequest) 对象。`view_func` 是一个 Django 将要使用的 Python 函数。（这是一个真实的函数对象，不是函数的名称）；`view_args` 是一个用来传递给视图的位置参数列表，；`view_kwargs` 是一个用来传递给视图的关键字参数字典。`view_args` 和 `view_kwargs` 都不包含第一个视图参数 ( `request` )。

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

它应该返回 `None` 或 [`HttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.HttpResponse) 对象。如果它返回 `None` ，Django 将继续处理这个请求，执行任何其他的 `process_view()` ，然后执行相应的视图。如果它返回 [`HttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.HttpResponse) 对象，Django 不会去影响调用相应的视图；它会将响应中间件应用到 [`HttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.HttpResponse) 并返回结果。

> **Note**
>
> 在视图运行前或在 `process_view()` 内访问中间件里的 [`request.POST`](/zh-hans/4.2/ref/request-response/#django.http.HttpRequest.POST) 将阻止中间件之后运行的任何视图修改请求的上传处理程序 （[modify the upload handlers for the request](/zh-hans/4.2/topics/http/file-uploads/#modifying-upload-handlers-on-the-fly)  ），通常应该避免这样。
>
> [`CsrfViewMiddleware`](/zh-hans/4.2/ref/middleware/#django.middleware.csrf.CsrfViewMiddleware) 类可以被视为一个例外，因为它提供 [`csrf_exempt()`](/zh-hans/4.2/ref/csrf/#django.views.decorators.csrf.csrf_exempt) 和 [`csrf_protect()`](/zh-hans/4.2/ref/csrf/#django.views.decorators.csrf.csrf_protect) 装饰器，它们允许视图完全控制 CSRF 验证在什么时候进行。

### `process_exception()`

#### `process_exception(request, exception)`

`request` 是一个 [`HttpRequest`](/zh-hans/4.2/ref/request-response/#django.http.HttpRequest) 对象。 `exception` 是一个由视图函数引发的 `Exception` 对象。

当视图引发异常时，Django 会调用 `process_exception()`。`process_exception()` 应该返回 `None` 或 [`HttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.HttpResponse) 对象。如果它返回一个 [`HttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.HttpResponse) 对象，模板响应和响应中间件将被应用且会将结果响应返回浏览器。否则，就会开始默认异常处理（ [default exception handling](/zh-hans/4.2/ref/views/#error-views) ）。

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

### `process_template_response()`

#### `process_template_response(request, response)`

`request` 是一个 [`HttpRequest`](/zh-hans/4.2/ref/request-response/#django.http.HttpRequest) 对象。`response` 是 [`TemplateResponse`](/zh-hans/4.2/ref/template-response/#django.template.response.TemplateResponse) 对象（或者等效对象），它通过 Django 视图或中间件返回。

`process_template_response()` 在视图被完全执行后调用，如果响应实例有 `render()` 方法，表明它是一个 [`TemplateResponse`](/zh-hans/4.2/ref/template-response/#django.template.response.TemplateResponse) 或等效对象。

它必须返回一个实现了 `render` 方法的响应对象。它可以通过改变\`\`response.template\_name\`\` 和 `response.context_data` 来改变给定的 `response` ，或者它可以创建和返回全新的 [`TemplateResponse`](/zh-hans/4.2/ref/template-response/#django.template.response.TemplateResponse) 或等效对象。

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

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

## 处理流式响应

与 [`HttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.HttpResponse) 不同，[`StreamingHttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.StreamingHttpResponse) 没有 `content` 属性。因此，中间件不能再假设所有响应有 `content` 属性。如果它们需要访问 content，它们必须为流式响应进行测试，并且调整其行为：

```
if response.streaming:
    response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
    response.content = alter_content(response.content)
```

> **Note**
>
> `streaming_content` 被假设为体积太大而无法在内存中保存。响应中间件可以将其包装在一个新的生成器里，但不能使用它。包装通常如下实现：
>
> ```
> def wrap_streaming_content(content):
>     for chunk in content:
>         yield alter_content(chunk)
> ```

[`StreamingHttpResponse`](/zh-hans/4.2/ref/request-response/#django.http.StreamingHttpResponse) 允许同步和异步迭代器。包装函数必须匹配。如果你的中间件需要支持两种类型的迭代器，请检查 [`StreamingHttpResponse.is_async`](/zh-hans/4.2/ref/request-response/#django.http.StreamingHttpResponse.is_async)。

> **Changed in Django 4.2**
>
> 支持使用异步迭代器的流式响应已经添加。

## 异常处理

Django 自动转换视图引发的异常，或者带有错误状态代码的特定 HTTP 响应内的中间件引发的异常。某些异常（ [Certain exceptions](/zh-hans/4.2/ref/views/#error-views) ）被转换为 4xx 状态代码，而未知异常被转换为 500 状态代码。

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

你可以将 [`DEBUG_PROPAGATE_EXCEPTIONS`](/zh-hans/4.2/ref/settings/#std-setting-DEBUG_PROPAGATE_EXCEPTIONS) 设置为 `True` 来跳过这个转换并将异常传播到上层。

## 异步支持

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

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

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

如果你的中间件同时设置了 `sync_capable = True` 和 `async_capable = True`，那么Django将直接传递请求而不进行转换。在这种情况下，你可以通过检查传递给你的 `get_response` 对象是否是协程函数，使用 `asgiref.sync.iscoroutinefunction` 来确定你的中间件是否会接收到异步请求。

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

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

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

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

```
from asgiref.sync import iscoroutinefunction
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 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
```

> **Note**
>
> 如果你声明了一个同时支持同步和异步调用的混合中间件，你得到的调用种类可能与底层视图不匹配。Django 会优化中间件调用栈，使其尽可能少的同步／异步转换。
>
> 因此，即使你包装的是一个异步视图，如果在你和视图之间有其他的、同步的中间件，你也可能会在同步模式下被调用。

在使用异步的基于类的中间件时，你必须确保实例被正确标记为协程函数：

```
from asgiref.sync import iscoroutinefunction, markcoroutinefunction

class AsyncMiddleware:
    async_capable = True
    sync_capable = False

    def __init__(self, get_response):
        self.get_response = get_response
        if iscoroutinefunction(self.get_response):
            markcoroutinefunction(self)

    async def __call__(self, request):
        response = await self.get_response(request)
        # Some logic ...
        return response
```

## 升级 Django 1.10 之前的中间件

#### `class django.utils.deprecation.MiddlewareMixin`

Django 提供了 `django.utils.deprecation.MiddlewareMixin` 来方便创建同时兼容 [`MIDDLEWARE`](/zh-hans/4.2/ref/settings/#std-setting-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`](/zh-hans/4.2/ref/settings/#std-setting-MIDDLEWARE) 和 `MIDDLEWARE_CLASSES` 在使用上有些行为差异：

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