pin_drop当前位置:知识文库 ❯ 图文

Django中间件详解 - 请求响应钩子系统完整指南

Django中间件详解 - 请求响应处理钩子系统

一、中间件概述

Django中间件(Middleware)是Django框架中用于处理HTTP请求和响应的钩子系统。它提供了一种轻量级、全局的机制,可以在请求到达视图之前和响应返回客户端之后执行自定义逻辑。中间件按照在MIDDLEWARE配置列表中的顺序依次执行,形成一个"洋葱式"的处理流程。

中间件的常见用途包括:用户认证与权限校验、请求日志记录、跨域资源共享(CORS)处理、接口限流防刷、全局异常处理、请求数据预处理、响应数据后处理等。它是Django扩展请求处理流程的核心机制,几乎所有Django项目都会使用到中间件。

小贴士

Django内置了多个中间件,如SecurityMiddleware(安全相关)、SessionMiddleware(会话管理)、CsrfViewMiddleware(CSRF防护)等,新建项目时会自动配置。

二、中间件语法与结构

从Django 1.10开始,中间件采用新式写法,基于类的__init____call__方法实现。以下是最基本的中间件结构:

代码示例

class MyMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # 请求处理(视图之前)
        response = self.get_response(request)
        # 响应处理(视图之后)
        return response

    def process_exception(self, request, exception):
        # 异常处理
        pass

settings.py中注册中间件:

代码示例

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'myproject.middleware.MyMiddleware',  # 自定义中间件
]

三、中间件方法详解

中间件核心方法

方法 调用时机 返回值 说明
__init__(get_response) 服务器启动 初始化,保存get_response引用
__call__(request) 每次请求 HttpResponse 处理请求和响应的核心方法
process_view() 调用视图前 None/HttpResponse 视图预处理,可跳过视图
process_exception() 视图抛异常 None/HttpResponse 捕获并处理视图异常
process_template_response() 模板响应 TemplateResponse 修改模板响应上下文

返回值说明

  • __call__:必须返回HttpResponse对象,不能返回None

  • process_view:返回None继续执行视图,返回HttpResponse则跳过视图

  • process_exception:返回None继续传播异常,返回HttpResponse停止传播

四、中间件执行流程

中间件的执行遵循"洋葱模型":请求从外到内逐层穿过中间件到达视图,响应从内到外逐层穿过中间件返回客户端。这种设计使得每个中间件都可以在请求和响应两个阶段介入处理。

代码示例

# 请求处理流程
请求 -> Middleware1(前) -> Middleware2(前) -> Middleware3(前) -> View -> 响应处理
                                                                        |
# 响应处理流程(逆序)                                                  |
响应 <- Middleware1(后) <- Middleware2(后) <- Middleware3(后) <----------+

MIDDLEWARE列表中,位置靠前的中间件先处理请求,但后处理响应。因此中间件的顺序至关重要,需要根据实际业务逻辑合理安排。

小贴士

认证相关的中间件应该放在前面,确保后续中间件可以访问用户信息;而日志中间件通常放在靠后位置,以便记录经过所有处理后的最终响应状态。

五、实战代码示例

示例1:请求计时中间件

这个中间件用于统计每个请求的处理时间,并将耗时信息添加到响应头中,同时将日志记录到日志系统。这是性能监控和优化中最常用的中间件类型。

代码示例

import time
import logging

logger = logging.getLogger(__name__)

class TimingMiddleware:
    """请求计时中间件"""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        start_time = time.time()

        response = self.get_response(request)

        duration = time.time() - start_time
        logger.info(
            f'{request.method} {request.path} - '
            f'{response.status_code} - {duration:.3f}s'
        )

        # 在响应头中添加处理时间
        response['X-Process-Time'] = f'{duration:.3f}s'
        return response

示例2:IP限流中间件

IP限流中间件用于防止恶意请求和接口滥用。它记录每个IP地址的请求时间戳,在指定时间窗口内超过阈值时返回403禁止访问。

代码示例

import time
from django.http import HttpResponseForbidden

class RateLimitMiddleware:
    """IP限流中间件"""

    def __init__(self, get_response):
        self.get_response = get_response
        self.requests = {}  # {ip: [timestamp1, timestamp2, ...]}
        self.max_requests = 100  # 最大请求数
        self.window = 60  # 时间窗口(秒)

    def __call__(self, request):
        ip = self.get_client_ip(request)
        now = time.time()

        # 清理过期记录
        if ip in self.requests:
            self.requests[ip] = [t for t in self.requests[ip] if now - t < self.window]
        else:
            self.requests[ip] = []

        # 检查限流
        if len(self.requests[ip]) >= self.max_requests:
            return HttpResponseForbidden('请求过于频繁,请稍后再试')

        self.requests[ip].append(now)
        return self.get_response(request)

    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            return x_forwarded_for.split(',')[0].strip()
        return request.META.get('REMOTE_ADDR')

示例3:异常处理中间件

异常处理中间件通过process_exception方法捕获视图层未处理的异常。对于API请求,返回格式化的JSON错误响应;对于普通页面请求,则交由Django默认错误处理机制。

代码示例

import logging
from django.conf import settings
from django.http import JsonResponse

logger = logging.getLogger(__name__)

class ExceptionHandlerMiddleware:
    """异常处理中间件"""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_exception(self, request, exception):
        """处理未捕获的异常"""
        logger.error(
            f'未处理异常: {exception.__class__.__name__}: {exception}',
            exc_info=True
        )

        # API请求返回JSON错误
        if request.path.startswith('/api/'):
            return JsonResponse({
                'error': '服务器内部错误',
                'detail': str(exception) if settings.DEBUG else None,
            }, status=500)

        # 普通请求返回None,由Django默认处理
        return None

六、实际应用场景

  • 请求日志记录:记录每个请求的方法、路径、状态码和响应时间,便于后续性能分析和故障排查

  • IP限流防护:防止恶意爬虫、暴力破解和DDoS攻击,保护API接口安全

  • 统一异常处理:集中处理应用层异常,为不同请求类型返回友好的错误响应格式

七、注意事项

注意:中间件在MIDDLEWARE列表中的顺序很重要,请求按顺序执行,响应按逆序执行。认证中间件应放在前面,日志中间件放在后面。

注意:中间件的__init__方法只在服务器启动时调用一次,不要在其中存储请求相关数据,否则会导致多线程请求间的状态污染。

注意:process_view返回HttpResponse会跳过视图函数,但列表中后续中间件的process_view仍会继续执行。

注意:新式中间件(基于__call__)和旧式中间件(基于process_request/process_response)不要混用,Django 3.0+已移除旧式中间件支持。

八、练习题

练习1

编写一个中间件,在每个响应头中添加X-Server-Version头,值为Django版本号。

练习2

编写一个中间件,记录每个请求的用户ID、请求路径和响应状态码到日志文件。如果用户未登录,用户ID记录为"anonymous"。


常见问题

Django中间件的执行顺序是怎样的?

中间件按照MIDDLEWARE列表从上到下的顺序处理请求,到达视图后,再按从下到上的逆序处理响应。因此列表最上方的中间件最先处理请求、最后处理响应。

中间件中可以跳过视图直接返回响应吗?

可以。在__call__方法中不调用self.get_response(request),直接返回HttpResponse对象即可跳过视图。这在限流、IP黑名单、权限校验等场景非常实用。

process_exception方法什么时候被调用?

当视图函数抛出未捕获的异常时,Django会从当前中间件开始逆序调用每个中间件的process_exception方法。如果某个中间件返回了HttpResponse,异常传播停止;返回None则继续传播。

如何在中间件中获取当前登录用户信息?

需要在SessionMiddleware和AuthenticationMiddleware之后的中间件中才能访问request.user。因为用户的认证信息是由这些内置中间件设置的。

中间件会影响性能吗?

每个中间件都会在请求-响应周期中增加处理时间。如果中间件逻辑复杂或包含I/O操作,会显著影响性能。建议保持中间件轻量,避免在中间件中进行数据库查询等耗时操作。

标签: Django 中间件 请求处理 限流 异常处理 Web开发

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Django Admin后台详解 - 从零搭建管理系统完整教程 下一篇: Django ORM查询详解 - 数据库操作与优化完整指南

poll相关推荐