django中间件系统 - 请求处理的拦截器模式

📂 所属阶段:第二部分 — 进阶特性
🎯 难度等级:中级
⏰ 预计学习时间:3-4小时
🎒 前置知识:视图系统与URL路由

目录


中间件概念与核心结构

django中间件是全局请求/响应拦截器框架——类似给请求流加了「安检站」和「收尾台」,能在不修改视图/模板的前提下,给整个应用加功能。

最简单的中间件长这样

从django 1.10开始,新风格中间件是主流,只需实现两个核心方法:

# myapp/middleware/simple.py
class SimpleCheckMiddleware:
    def __init__(self, get_response):
        """django启动时**仅执行1次**的初始化
        get_response:下一个中间件或视图的入口函数
        """
        self.get_response = get_response
        # 这里可以做一次性资源加载(如编译正则、初始化缓存)

    def __call__(self, request):
        """**每个请求都会执行**的核心逻辑
        分为「请求前拦截」「放行」「响应后处理」三段
        """
        # ------------------- 请求前拦截 -------------------
        # 例如:检查是否是黑名单IP
        blacklist = ["192.168.1.100"]
        client_ip = request.META.get("REMOTE_ADDR")
        if client_ip in blacklist:
            from django.http import HttpResponseForbidden
            return HttpResponseForbidden("您的IP已被封禁")

        # ------------------- 放行(交给下一个环节) -------------------
        response = self.get_response(request)

        # ------------------- 响应后处理 -------------------
        # 例如:给所有响应加安全头
        response["X-Content-Type-Options"] = "nosniff"

        return response

中间件执行流程与配置

洋葱模型:核心执行逻辑

中间件的执行是「洋葱结构」——配置列表靠前的先拦请求,后拦响应

"""
MIDDLEWARE = [
    '中间件A',  # 1. 拦截请求;5. 拦截响应
    '中间件B',  # 2. 拦截请求;4. 拦截响应
    '视图层',   # 3. 处理业务
]
"""

必须在 settings.py 注册

自定义中间件不注册=没用,推荐放在列表最后,避免破坏内置中间件的依赖关系:

# 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',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    
    # 【自定义中间件】放最后!
    'myapp.middleware.simple.SimpleCheckMiddleware',
]

高频内置中间件速览

django默认带了10+内置中间件,不用全学,重点掌握这5个:

1. SecurityMiddleware(安全拦截第一站)

自动加HTTPS、HSTS、XSS防护等安全头,生产环境必须保留。

# 生产环境常用配置
SECURE_SSL_REDIRECT = True  # 强制跳转HTTPS
SECURE_HSTS_SECONDS = 31536000  # 浏览器记住1年只走HTTPS

2. SessionMiddleware(会话管理)

给每个请求/响应加sessionidCookie,关联服务端的会话数据,必须放在AuthenticationMiddleware前面。

3. CsrfViewMiddleware(防跨站伪造)

检查POST/PUT/PATCH请求的CSRF令牌,防止恶意网站盗用用户身份。

# 允许跨站请求的来源(生产环境必须白名单!)
CSRF_TRUSTED_ORIGINS = ["https://your-frontend.com"]

4. AuthenticationMiddleware(身份注入)

从会话中提取用户信息,注入到request.user(已认证是User对象,未认证是AnonymousUser)。

5. CommonMiddleware(通用功能)

自动处理URL尾部斜杠、APPEND_SLASH配置,生产环境建议保留。


自定义中间件实战

选两个开发中100%会用到的场景:

1. 慢请求/异常日志中间件

记录每个请求的耗时、状态码、IP,便于排查问题:

# myapp/middleware/logging.py
import logging
import time
from django.utils.deprecation import MiddlewareMixin  # 兼容旧django写法(可选)

logger = logging.getLogger("django.request")  # 用django自带的请求日志器

class RequestLogMiddleware(MiddlewareMixin):
    def process_request(self, request):
        """请求前:记录开始时间"""
        request.start_time = time.time()

    def process_response(self, request, response):
        """响应后:记录耗时、状态码"""
        if hasattr(request, "start_time"):
            duration = time.time() - request.start_time
            # 慢请求单独记录(超过1秒)
            if duration > 1.0:
                logger.warning(
                    f"SLOW REQUEST: {request.method} {request.path} | "
                    f"Duration: {duration:.2f}s | Status: {response.status_code} | "
                    f"IP: {self.get_client_ip(request)}"
                )
            else:
                logger.info(
                    f"REQUEST: {request.method} {request.path} | "
                    f"Status: {response.status_code} | Duration: {duration:.2f}s"
                )
        return response

    def process_exception(self, request, exception):
        """异常时:记录堆栈信息"""
        logger.error(
            f"EXCEPTION: {request.method} {request.path} | "
            f"Error: {str(exception)}",
            exc_info=True  # 必须加!才会输出完整堆栈
        )
        return None  # 让后续中间件继续处理异常(或返回自定义响应)

    def get_client_ip(self, request):
        """兼容有反向代理的场景(如Nginx)"""
        x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
        return x_forwarded_for.split(",")[0] if x_forwarded_for else request.META.get("REMOTE_ADDR")

2. 简易接口限流中间件

防止恶意刷接口(简单版,生产环境推荐用django-ratelimit库):

# myapp/middleware/rate_limit.py
import time
from collections import defaultdict
from django.http import HttpResponse

class SimpleRateLimitMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.request_history = defaultdict(list)  # {ip: [timestamp1, timestamp2]}
        self.max_requests = 60  # 每分钟最多60次
        self.window_seconds = 60  # 时间窗口1分钟

    def __call__(self, request):
        # 只限制API接口(可根据实际情况调整)
        if not request.path.startswith("/api/"):
            return self.get_response(request)

        client_ip = self.get_client_ip(request)
        current_time = time.time()

        # 1. 清理1分钟前的旧记录
        self.request_history[client_ip] = [
            t for t in self.request_history[client_ip]
            if current_time - t < self.window_seconds
        ]

        # 2. 检查是否超过限制
        if len(self.request_history[client_ip]) >= self.max_requests:
            return HttpResponse(
                "Too Many Requests",
                status=429,
                headers={"Retry-After": str(self.window_seconds)}
            )

        # 3. 记录当前请求
        self.request_history[client_ip].append(current_time)

        # 4. 放行
        return self.get_response(request)

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

执行顺序与最佳实践

顺序红线(踩了必出问题)

  1. SessionMiddleware → AuthenticationMiddleware:认证依赖会话
  2. SecurityMiddleware → 所有其他中间件:安全第一
  3. CommonMiddleware → CsrfViewMiddleware:通用处理依赖URL标准化

性能与安全最佳实践

  1. 初始化只做1次__init__里加载静态资源、编译正则,不要在__call__里查全表
  2. 快速返回:用正则或前缀判断提前拦截不需要处理的请求(如静态文件)
  3. 不要吞异常:除非有明确的处理逻辑,否则用logger.error后继续抛出
  4. 自定义中间件放最后:避免破坏内置中间件的依赖链

常见坑点排查

坑1:自定义中间件没生效

  • 排查1:检查MIDDLEWARE列表的路径是否正确(注意.分隔,不是/
  • 排查2:检查中间件类是否有__init__(self, get_response)__call__(self, request)方法
  • 排查3:检查是否有语法错误(启动django会报错)

坑2:CSRF验证失败

  • 排查1:检查表单/请求是否带了{% csrf_token %}X-CSRFToken请求头
  • 排查2:检查反向代理是否把HTTP_X_CSRFTOKEN透传给django
  • 排查3:检查CSRF_TRUSTED_ORIGINS是否包含前端域名(生产环境必须HTTPS)

坑3:request.userAnonymousUser

  • 排查1:检查SessionMiddleware是否在AuthenticationMiddleware前面
  • 排查2:检查会话是否过期(SESSION_COOKIE_AGE
  • 排查3:检查是否通过login()方法登录了

本章小结

本章我们掌握了django中间件的核心知识:

  1. 概念:全局请求/响应拦截器,类似洋葱模型
  2. 结构:新风格只需__init____call__,旧风格用MiddlewareMixin
  3. 内置中间件:重点学Security、Session、Csrf、Authentication、Common
  4. 实战:写了日志和限流两个常用中间件
  5. 坑点:顺序不对、路径错误、CSRF配置问题

💡 核心提醒:中间件是「双刃剑」——能快速加全局功能,但加太多会拖慢请求!尽量只加必要的。


🔗 相关教程

🏷️ 标签django中间件 请求处理 拦截器 自定义中间件 django安全