Python · 2 分钟阅读
中间件基础
中间件是"请求/响应"链路上的钩子函数,按
settings.MIDDLEWARE顺序串成一条链。
5 个钩子
| 钩子 | 触发时机 | 用途 |
|---|---|---|
process_request |
路由前 | 拦截、限流、注入 |
process_view |
视图前 | 鉴权、注入上下文 |
process_response |
视图后 | 改响应头、统一包装 |
process_exception |
视图抛异常 | 统一异常处理 |
process_template_response |
视图返回 TemplateResponse | 渲染前最后修改 |
执行顺序
请求 → MW1.process_request → MW2.process_request → ... → MW2.process_view → MW1.process_view → View
响应 ← MW1.process_response ← MW2.process_response ← ... ← MW2.process_view(返回) ← MW1.process_view(返回)
总结:请求自上而下,响应自下而上。
快速上手
# core/middleware.py
import time
class TimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start = time.perf_counter()
response = self.get_response(request)
duration = (time.perf_counter() - start) * 1000
response["X-Request-Time-ms"] = f"{duration:.2f}"
return response
# settings.py
MIDDLEWARE = [
"core.middleware.TimingMiddleware",
# ... 其它中间件
]
process_request 返回值
- 返回
None— 继续往下走 - 返回
HttpResponse— 立即返回,跳过后续中间件和视图
class IPBlockMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.blocked = {"1.2.3.4"}
def __call__(self, request):
ip = request.META.get("REMOTE_ADDR")
if ip in self.blocked:
from django.http import HttpResponseForbidden
return HttpResponseForbidden("Blocked")
return self.get_response(request)
现代写法(new-style)
Django 1.10+ 推荐的"可调用对象"写法简洁高效,99% 的场景无需再写 process_request / process_response 分开的方法。
注意事项
- 中间件顺序敏感(特别是
SessionMiddleware必须在AuthenticationMiddleware之前) - 不要在中间件里做"重活",会拖慢每次请求
- 异步视图(
async def view)请使用async def __call__