django middleware system - interceptor mode for request processing | Daoman PythonAI

#django middleware system - interceptor mode for request processing

📂 Stage: Part 2 - Advanced Features 🎯 Difficulty level: Intermediate ⏰ Estimated study time: 3-4 hours 🎒 Prerequisite knowledge: 视图系统与URL路由

Table of contents


Middleware concept and core structure

Django middleware is a global request/response interceptor framework - similar to adding a "security checkpoint" and "finishing station" to the request flow, which can add functions to the entire application without modifying the view/template.

The simplest middleware looks like this

Starting from Django 1.10, new style middleware is mainstream, and only needs to implement two core methods:

# 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 execution process and configuration

Onion model: core execution logic

The execution of the middleware is "onion structure"-the top one in the configuration list blocks the request first, and then blocks the response**:

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

Must be insettings.pyregister

Custom middleware not registered = useless, it is recommended to put it at the end of the list to avoid destroying the dependencies of the built-in middleware:

# 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',
]

A quick overview of high-frequency built-in middleware

Django comes with 10+ built-in middleware by default. You don’t need to learn them all. Focus on mastering these 5:

1. SecurityMiddleware (the first stop for security interception)

Automatically add HTTPS, HSTS, XSS protection and other security headers, which must be retained in the production environment.

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

2. SessionMiddleware (session management)

Add to each request/responsesessionidCookie, the session data associated with the server, must be placed inAuthenticationMiddlewareFront.

3. CsrfViewMiddleware (anti-cross-site forgery)

Check the CSRF token of POST/PUT/PATCH requests to prevent malicious websites from stealing user identities.

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

4. AuthenticationMiddleware (identity injection)

Extract user information from the session and inject it intorequest.user(Certified to beUserObject, unauthenticated isAnonymousUser)。

5. CommonMiddleware (common function)

Automatically handles URL trailing slashes and APPEND_SLASH configuration. It is recommended to retain them in production environments.


Customized middleware in action

Choose two scenarios that will be used 100% in development:

1. Slow request/exception log middleware

Record the time consumption, status code, and IP of each request to facilitate troubleshooting:

# 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. Simple interface current limiting middleware

Prevent malicious interface brushing (simple version, recommended for production environments)django-ratelimitlibrary):

# 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")

Execution sequence and best practices

Sequential red line (if you step on it, there will be problems)

  1. SessionMiddleware → AuthenticationMiddleware: Authentication relies on session
  2. SecurityMiddleware → All other middleware: Security first
  3. CommonMiddleware → CsrfViewMiddleware: Common processing relies on URL standardization

Performance and Security Best Practices

  1. Initialization is only done once:__init__Load static resources and compile regular rules in it. Do not load it in__call__Check out the full list
  2. Quick Return: Use regular or prefix judgment to intercept requests that do not need to be processed (such as static files) in advance.
  3. Don’t swallow exceptions: Unless there is clear processing logic, otherwise uselogger.errorcontinue to throw after
  4. Put custom middleware last: avoid damaging the dependency chain of built-in middleware

Common pitfall troubleshooting

Pit 1: Custom middleware does not take effect

  • Troubleshooting 1: CheckMIDDLEWAREIs the path to the list correct (note.Separate, not/
  • Troubleshooting 2: Check whether the middleware class exists__init__(self, get_response)and__call__(self, request)method
  • Troubleshooting 3: Check if there are any syntax errors (an error will be reported when starting django)

Pit 2: CSRF verification failed

  • Troubleshooting 1: Check whether the form/request is included{% csrf_token %}orX-CSRFTokenRequest header
  • Troubleshooting 2: Check whether the reverse proxyHTTP_X_CSRFTOKENTransparently passed to django
  • Troubleshooting 3: CheckCSRF_TRUSTED_ORIGINSWhether to include the front-end domain name (the production environment must be HTTPS)

Pit 3:request.useryesAnonymousUser

  • Troubleshooting 1: CheckSessionMiddlewareIs thereAuthenticationMiddlewareFront
  • Troubleshooting 2: Check whether the session has expired (SESSION_COOKIE_AGE
  • Troubleshooting 3: Check whether it passeslogin()Method logged in

Summary of this chapter

In this chapter we have mastered the core knowledge of Django middleware:

  1. Concept: Global request/response interceptor, similar to the onion model
  2. Structure: The new style only needs__init__and__call__, the old style usesMiddlewareMixin
  3. Built-in middleware: Focus on Security, Session, CSRF, Authentication, and Common
  4. Practical: Wrote two common middlewares: logging and current limiting.
  5. ** Pitfalls **: Wrong order, wrong path, CSRF configuration issues

💡 Core Reminder: Middleware is a "double-edged sword" - it can quickly add global functions, but adding too much will slow down requests! Try to add only what is necessary.


🔗 Related tutorials

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