一、什么是Django中间件

想象一下你每天上班要经过的安检通道,所有进入大楼的人都要先经过安检,这就是中间件的作用。在Django中,中间件就像是请求和响应之间的安检通道,每个请求和响应都会经过这些通道。

中间件本质上是一个轻量级的插件系统,可以全局改变Django的输入和输出。它位于Django处理请求和响应的核心流程中,能够拦截所有经过Django的请求和响应。

二、为什么要自定义中间件

假设我们需要实现以下功能:

  1. 记录每个请求的处理时间
  2. 检查所有请求是否携带特定header
  3. 对所有响应统一添加CORS头
  4. 对特定路径的请求进行预处理

这些需求如果放在视图函数中处理,会导致大量重复代码。中间件正是解决这类问题的完美方案,它让我们可以在一个地方集中处理这些逻辑。

三、如何编写自定义中间件

让我们通过一个完整示例来学习。下面是一个记录请求处理时间的中间件:

# 技术栈: Django 4.2

import time
from django.utils.deprecation import MiddlewareMixin

class RequestTimeMiddleware(MiddlewareMixin):
    """
    记录请求处理时间的中间件
    1. 在请求开始时记录时间
    2. 在响应结束时计算处理耗时
    3. 将耗时信息添加到响应头中
    """
    
    def process_request(self, request):
        # 在请求开始时记录当前时间
        request.start_time = time.time()
        return None
    
    def process_response(self, request, response):
        # 计算请求处理总耗时(毫秒)
        duration = round((time.time() - request.start_time) * 1000, 2)
        # 将耗时信息添加到响应头
        response['X-Request-Duration'] = f"{duration}ms"
        return response

这个中间件做了三件事:

  1. 在请求到达时记录开始时间
  2. 在响应返回时计算耗时
  3. 将耗时信息添加到响应头

四、中间件的五种钩子方法

Django中间件提供了五个关键方法,对应请求/响应处理的不同阶段:

  1. process_request - 请求到达视图前
  2. process_view - 路由找到视图后,执行视图前
  3. process_template_response - 视图返回模板响应后
  4. process_exception - 视图抛出异常时
  5. process_response - 响应返回客户端前

下面是一个使用多个钩子的完整示例:

# 技术栈: Django 4.2

from django.http import JsonResponse

class AuthCheckMiddleware(MiddlewareMixin):
    """
    综合认证检查中间件
    1. 检查请求头中的API-KEY
    2. 记录未认证访问尝试
    3. 对特定路径跳过检查
    """
    
    WHITELIST_PATHS = ['/api/public/', '/healthcheck/']
    
    def process_request(self, request):
        # 跳过白名单路径
        if any(request.path.startswith(p) for p in self.WHITELIST_PATHS):
            return None
            
        # 检查API-KEY头
        api_key = request.headers.get('API-KEY')
        if not api_key or api_key != 'SECRET_KEY_123':
            # 记录未认证访问
            print(f"Unauthorized access attempt to {request.path}")
            return JsonResponse({'error': 'Invalid API Key'}, status=401)
        
        return None
    
    def process_view(self, request, view_func, view_args, view_kwargs):
        # 可以在这里获取视图的更多信息
        print(f"Accessing view: {view_func.__name__}")
        return None

五、中间件的执行顺序

中间件的执行顺序很重要,它取决于MIDDLEWARE配置中的顺序。Django会按照以下顺序执行:

  1. 请求阶段: MIDDLEWARE列表从上到下
  2. 响应阶段: 从下到上

例如配置:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'myapp.middleware.RequestTimeMiddleware',  # 第二个执行
    'django.middleware.common.CommonMiddleware',
]

在这个例子中,RequestTimeMiddlewareprocess_request会在SecurityMiddleware之后执行,但它的process_response会在SecurityMiddleware之前执行。

六、实际应用场景

  1. 性能监控 - 记录请求耗时,慢请求报警
  2. 认证授权 - 统一处理JWT验证,权限检查
  3. 流量控制 - 实现API限流
  4. 数据预处理 - 请求数据清洗,格式化
  5. 响应处理 - 统一错误格式,添加标准头

下面是一个限流中间件的示例:

# 技术栈: Django 4.2

from django.core.cache import cache
from django.http import JsonResponse

class RateLimitMiddleware:
    """
    API限流中间件
    1. 基于IP的请求限流
    2. 滑动窗口算法实现
    3. 返回429状态码和剩余时间
    """
    
    def __init__(self, get_response):
        self.get_response = get_response
        self.WINDOW_SIZE = 60  # 60秒窗口
        self.MAX_REQUESTS = 100  # 最大100请求/分钟
    
    def __call__(self, request):
        ip = request.META.get('REMOTE_ADDR')
        cache_key = f"ratelimit:{ip}"
        
        # 获取当前请求计数
        current = cache.get(cache_key, 0)
        if current >= self.MAX_REQUESTS:
            # 计算剩余时间
            ttl = cache.ttl(cache_key)
            return JsonResponse(
                {'error': 'Too many requests', 'retry_after': ttl},
                status=429
            )
            
        # 增加计数(如果key不存在,会自动创建)
        cache.set(cache_key, current + 1, self.WINDOW_SIZE)
        
        return self.get_response(request)

七、技术优缺点分析

优点:

  1. 代码复用 - 一处实现,全局生效
  2. 解耦 - 业务逻辑与横切关注点分离
  3. 灵活 - 可以随时启用/禁用中间件
  4. 性能 - 比在每个视图单独处理更高效

缺点:

  1. 调试困难 - 错误可能发生在中间件链的任何位置
  2. 顺序敏感 - 错误的顺序可能导致意外行为
  3. 全局影响 - 一个坏的中间件可能影响整个应用

八、注意事项

  1. 性能影响 - 中间件会增加请求处理时间,避免复杂逻辑
  2. 异常处理 - 妥善处理中间件中的异常,避免500错误
  3. 测试覆盖 - 中间件需要单独测试,模拟请求/响应对象
  4. 依赖顺序 - 确保中间件按正确顺序加载
  5. 禁用场景 - 某些管理命令不会加载中间件

九、总结

Django中间件是一个强大的工具,能够优雅地处理横跨整个应用的关注点。通过本文的示例,我们学习了如何创建各种实用的中间件。记住中间件的核心思想是"拦截-处理-传递",合理使用可以让代码更干净、更可维护。

在实际项目中,建议从简单需求开始,逐步构建更复杂的中间件。同时要注意中间件的执行顺序和性能影响,避免过度使用导致系统难以维护。