Django 中间件

Django 中间件简介

Django 中间件是一个轻量级的、底层的插件系统,用于全局修改 Django 的输入或输出。每个中间件组件负责完成某个特定的功能,例如会话管理、身份验证、CSRF保护等。

1
请求到达
HTTP 请求进入
2
中间件处理
按顺序处理请求
3
视图处理
业务逻辑执行
4
响应返回
中间件处理响应
中间件的优势
  • 代码复用 - 跨视图的通用功能
  • 关注点分离 - 横切关注点的统一处理
  • 灵活性 - 可插拔的组件架构
  • 全局处理 - 对所有请求的统一操作
  • 执行顺序控制 - 精确控制处理流程
中间件类型
类型 描述 示例
请求中间件 处理传入请求 身份验证、会话
响应中间件 处理传出响应 Gzip压缩、缓存
安全中间件 安全相关处理 CSRF保护、XSS防护
处理中间件 处理流程控制 缓存、消息

中间件执行流程

Django 中间件按照在 MIDDLEWARE 设置中定义的顺序执行,形成一个处理管道。

完整的执行流程
请求阶段

中间件按顺序处理请求

视图处理

调用对应的视图函数

异常处理

处理视图抛出的异常

响应阶段

中间件按逆序处理响应

响应返回

返回最终的 HTTP 响应

执行顺序示例
执行顺序可视化
# settings.py 中的中间件顺序
MIDDLEWARE = [
'middleware1',  # 1. 最先处理请求
'middleware2',  # 2.
'middleware3',  # 3.
# ...
'middlewareN',  # N. 最后处理请求
]

# 执行流程:
# 请求 → middleware1 → middleware2 → ... → 视图
# 响应 ← middlewareN ← ... ← middleware2 ← middleware1
中间件钩子方法
中间件类结构
class SimpleMiddleware:
def __init__(self, get_response):
    # 初始化,只在服务器启动时执行一次
    self.get_response = get_response
    # 可以在这里进行一次性初始化

def __call__(self, request):
    # 在每个请求调用视图之前执行
    # 请求处理逻辑

    response = self.get_response(request)

    # 在每个响应返回给客户端之前执行
    # 响应处理逻辑

    return response

def process_view(self, request, view_func, view_args, view_kwargs):
    # 在调用视图之前,但在 URL 解析之后执行
    # 可以返回 None 或 HttpResponse 对象
    pass

def process_exception(self, request, exception):
    # 当视图抛出异常时调用
    # 可以返回 None 或 HttpResponse 对象
    pass

def process_template_response(self, request, response):
    # 在视图执行完成后调用,如果响应有 render 方法
    # 必须返回实现了 render 方法的响应对象
    return response
钩子方法执行时机:
  • __call__ 必须 - 每个请求都会调用
  • process_view 可选 - 视图调用前
  • process_exception 可选 - 异常发生时
  • process_template_response 可选 - 模板响应时

Django 内置中间件

Django 提供了多个内置中间件来处理常见的 Web 应用需求。

常用内置中间件

中间件 功能 配置 顺序建议
SecurityMiddleware 安全相关功能,如 SSL 重定向、安全头设置 通常第一个 1 最早
SessionMiddleware 会话支持 在 AuthenticationMiddleware 之前 2
CommonMiddleware 常用功能,如 URL 重写、ETag 支持 位置灵活 3
CsrfViewMiddleware CSRF 保护 在视图中间件之前 4
AuthenticationMiddleware 用户认证,添加 request.user 在 SessionMiddleware 之后 5
MessageMiddleware 消息框架支持 在 SessionMiddleware 之后 6
XFrameOptionsMiddleware 点击劫持保护(已合并到 SecurityMiddleware) 已弃用 -

中间件配置示例

settings.py - 中间件配置
MIDDLEWARE = [
# 安全中间件应该在最前面
'django.middleware.security.SecurityMiddleware',

# 会话支持
'django.contrib.sessions.middleware.SessionMiddleware',

# 通用中间件
'django.middleware.common.CommonMiddleware',

# CSRF 保护
'django.middleware.csrf.CsrfViewMiddleware',

# 用户认证
'django.contrib.auth.middleware.AuthenticationMiddleware',

# 消息框架
'django.contrib.messages.middleware.MessageMiddleware',

# 点击劫持保护 (旧版本)
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',

# 自定义中间件
'myapp.middleware.CustomMiddleware',
]

# SecurityMiddleware 配置
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_SSL_REDIRECT = False  # 生产环境设为 True
SECURE_HSTS_SECONDS = 31536000  # 1年
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# SessionMiddleware 配置
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SESSION_COOKIE_AGE = 1209600  # 2周,默认
SESSION_COOKIE_SECURE = True  # 仅HTTPS
SESSION_COOKIE_HTTPONLY = True

# CommonMiddleware 配置
APPEND_SLASH = True
PREPEND_WWW = False
中间件功能详解
SecurityMiddleware
  • 设置安全相关的 HTTP 头
  • SSL/HTTPS 重定向
  • HSTS (HTTP Strict Transport Security)
  • 内容类型嗅探保护
  • XSS 保护
SessionMiddleware
  • 为每个请求添加 request.session
  • 会话数据存储和检索
  • 会话Cookie管理
  • 支持多种会话后端
CommonMiddleware
  • URL 规范化(添加/移除斜杠)
  • ETag 支持
  • 禁止用户代理
  • 连接保持(Keep-Alive)
CsrfViewMiddleware
  • CSRF 令牌验证
  • 保护 POST、PUT、DELETE 请求
  • 自动添加 CSRF token 到表单
  • 豁免特定视图
注意: 中间件的顺序非常重要,错误的顺序可能导致功能异常或安全漏洞。

自定义中间件

创建自定义中间件可以处理应用特定的横切关注点。

基本自定义中间件

myapp/middleware.py
import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

class SimpleTimingMiddleware:
"""简单的计时中间件"""

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

    # 添加自定义头
    response['X-Request-Duration'] = f'{duration:.2f}s'

    # 记录日志(生产环境应使用 logging)
    print(f'{request.method} {request.path} - {duration:.2f}秒')

    return response

class MaintenanceModeMiddleware:
"""维护模式中间件"""

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

def __call__(self, request):
    # 检查是否启用维护模式
    if self.is_maintenance_mode():
        # 排除管理员和特定路径
        if (request.user.is_staff or
            request.path.startswith('/admin/') or
            request.path == '/maintenance/'):
            return self.get_response(request)

        # 返回维护页面
        return HttpResponse(
            '网站正在维护中,请稍后访问...',
            status=503,
            content_type='text/html'
        )

    return self.get_response(request)

def is_maintenance_mode(self):
    # 从设置或数据库检查维护模式状态
    from django.conf import settings
    return getattr(settings, 'MAINTENANCE_MODE', False)
使用所有钩子方法的中间件
class AdvancedMiddleware:
"""使用所有钩子方法的中间件"""

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

def __call__(self, request):
    # 请求预处理
    print(f"开始处理请求: {request.method} {request.path}")

    # 添加请求属性
    request.processed_by_middleware = True

    response = self.get_response(request)

    # 响应后处理
    print(f"请求处理完成: {response.status_code}")

    return response

def process_view(self, request, view_func, view_args, view_kwargs):
    """在调用视图之前执行"""
    print(f"即将调用视图: {view_func.__name__}")

    # 可以在这里进行权限检查或其他预处理
    # 如果返回 HttpResponse,将跳过视图执行
    # return None 表示继续正常流程

def process_exception(self, request, exception):
    """处理视图异常"""
    print(f"捕获异常: {exception}")

    # 可以记录异常或返回自定义错误页面
    # 如果返回 HttpResponse,异常将被处理
    # return None 表示继续传播异常

def process_template_response(self, request, response):
    """处理模板响应"""
    if hasattr(response, 'render'):
        # 可以修改响应上下文或添加额外数据
        print("处理模板响应")

    return response
注册自定义中间件
settings.py
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',

# 自定义中间件
'myapp.middleware.SimpleTimingMiddleware',
'myapp.middleware.MaintenanceModeMiddleware',
'myapp.middleware.AdvancedMiddleware',
]

实用中间件示例

以下是一些在实际项目中常用的自定义中间件示例。

用户活动追踪中间件

活动追踪中间件
from django.utils import timezone
from .models import UserActivity

class UserActivityMiddleware:
"""记录用户活动中间件"""

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

def __call__(self, request):
    response = self.get_response(request)

    # 只记录认证用户的访问
    if request.user.is_authenticated:
        self.record_activity(request)

    return response

def record_activity(self, request):
    """记录用户活动"""
    try:
        UserActivity.objects.create(
            user=request.user,
            path=request.path,
            method=request.method,
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            ip_address=self.get_client_ip(request),
            timestamp=timezone.now()
        )
    except Exception as e:
        # 记录错误但不中断请求
        print(f"记录用户活动失败: {e}")

def get_client_ip(self, request):
    """获取客户端IP地址"""
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip
跨域中间件 (CORS)
class CorsMiddleware:
"""简单的 CORS 中间件"""

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

def __call__(self, request):
    response = self.get_response(request)

    # 添加 CORS 头
    response['Access-Control-Allow-Origin'] = '*'
    response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
    response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
    response['Access-Control-Allow-Credentials'] = 'true'

    return response

def process_view(self, request, view_func, view_args, view_kwargs):
    """处理 OPTIONS 预检请求"""
    if request.method == 'OPTIONS':
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
        response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
        response['Access-Control-Allow-Credentials'] = 'true'
        return response

    return None
提示: 对于复杂的 CORS 需求,建议使用 django-cors-headers 包。

更多实用中间件

IP限制中间件
class IPRestrictionMiddleware:
"""IP地址限制中间件"""

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

def __call__(self, request):
    client_ip = self.get_client_ip(request)

    # 检查IP是否在黑名单中
    if self.is_ip_blocked(client_ip):
        return HttpResponse(
            '您的IP地址已被限制访问',
            status=403
        )

    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:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

def is_ip_blocked(self, ip):
    # 从数据库或设置中检查IP黑名单
    blocked_ips = getattr(settings, 'BLOCKED_IPS', [])
    return ip in blocked_ips
请求日志中间件
import logging
import json

logger = logging.getLogger('django.request')

class RequestLoggingMiddleware:
"""请求日志中间件"""

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

def __call__(self, request):
    # 记录请求信息
    log_data = {
        'method': request.method,
        'path': request.path,
        'user_agent': request.META.get('HTTP_USER_AGENT', ''),
        'ip': self.get_client_ip(request),
        'user': str(request.user) if request.user.is_authenticated else 'anonymous'
    }

    logger.info(f"请求开始: {json.dumps(log_data)}")

    response = self.get_response(request)

    # 记录响应信息
    logger.info(f"请求完成: {request.method} {request.path} - {response.status_code}")

    return response

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

def process_exception(self, request, exception):
    """记录异常"""
    logger.error(
        f"请求异常: {request.method} {request.path} - {str(exception)}",
        exc_info=True
    )

中间件最佳实践

性能考虑
  • 避免在中间件中进行耗时操作
  • 使用缓存减少重复计算
  • 合理设置中间件顺序
  • 只在必要时处理请求
  • 考虑使用异步中间件
# 使用缓存
from django.core.cache import cache

def __call__(self, request):
    cache_key = f"middleware_{request.path}"
    result = cache.get(cache_key)
    if result is None:
        # 计算并缓存结果
        cache.set(cache_key, result, 300)
错误处理
  • 妥善处理异常,避免中断请求
  • 记录详细的错误日志
  • 提供有意义的错误响应
  • 使用适当的HTTP状态码
  • 考虑降级策略
def __call__(self, request):
    try:
        response = self.get_response(request)
    except Exception as e:
        logger.error(f"中间件错误: {e}")
        # 返回错误响应或继续传播异常
        return HttpResponseServerError()
安全考虑
  • 验证和清理所有输入数据
  • 避免信息泄露在响应头中
  • 正确处理敏感信息
  • 实施适当的访问控制
  • 定期审查中间件代码
# 安全的响应头设置
response['X-Custom-Header'] = 'value'
# 避免泄露敏感信息
# response['X-Server-Path'] = sensitive_path # 不安全!
测试和调试
  • 为中间件编写单元测试
  • 使用 Django 测试客户端
  • 记录详细的调试信息
  • 使用中间件特定的设置
  • 监控中间件性能
# 测试中间件
from django.test import RequestFactory

def test_middleware(self):
    factory = RequestFactory()
    request = factory.get('/')
    middleware = MyMiddleware(lambda r: HttpResponse())
    response = middleware(request)
    self.assertEqual(response.status_code, 200)

常见问题和解决方案

常见问题
问题 原因 解决方案
中间件不执行 顺序错误或未注册 检查 MIDDLEWARE 设置和顺序
AttributeError 缺少必需的请求属性 确保依赖的中间件在前面
性能下降 中间件处理耗时操作 优化代码或使用缓存
循环导入 中间件导入模型或其他应用 使用懒加载或重构代码
调试技巧
  • 使用 Django 调试工具栏
  • 添加详细的日志记录
  • 使用中间件特定的测试设置
  • 检查中间件执行顺序
  • 使用断点调试
# 调试中间件执行
class DebugMiddleware:
    def __call__(self, request):
        print(f"Debug: 处理请求 {request.path}")
        response = self.get_response(request)
        print(f"Debug: 响应状态 {response.status_code}")
        return response
生产环境注意事项:
  • 禁用调试中间件
  • 确保错误处理完善
  • 监控中间件性能
  • 定期审查安全设置