Django中间件通过process_request或__call__方法在请求到达视图前检查并主动拒绝非法请求,返回HttpResponse即中断流程;常见判断依据包括IP黑名单、User-Agent特征、缺失必要Header等,优先使用request.resolver_match获取解析后的视图名而非硬编码路径。

中间件怎么判断请求是否非法?
关键不是“拦截”,而是“在请求到达视图前做检查并主动拒绝”。Django中间件通过 process_request 或更推荐的 __call__ 方法介入,返回 HttpResponse 即可中断流程——不调用后续中间件,也不进视图。
- 常见判断依据:IP黑名单(
request.META.get("REMOTE_ADDR"))、User-Agent特征(如含"sqlmap"或"curl"但无 Referer)、缺失必要 Header(如request.META.get("HTTP_X_API_KEY")为空) - 别依赖
request.path做硬编码路径过滤,URL 可能被反向代理重写;优先用request.resolver_match获取解析后的view_name或url_name - 避免在中间件里做耗时操作(如查数据库),否则所有请求都变慢;IP 黑名单建议用内存集合(
set)或 Redis 缓存
如何写一个带白名单的 API 请求校验中间件?
很多场景是“只放行特定接口”,比如仅允许 /api/v1/status/ 和带有效 token 的 /api/v1/data/。直接在中间件里做逻辑比靠装饰器更统一。
- 先定义白名单路径和绕过条件:
WHITELIST_PATHS = {"/api/v1/status/", "/healthz/"};再检查request.path是否在其中,是则直接return None - 对需鉴权的路径(如
/api/v1/data/),提取request.META.get("HTTP_AUTHORIZATION"),验证 JWT 或 API Key;失败就返回HttpResponseForbidden("Invalid token") - 注意:Django 默认不解析
Authorization头为小写键,必须用HTTP_AUTHORIZATION,不是authorization
为什么中间件没生效?常见配置陷阱
中间件顺序决定行为,Django 按 MIDDLEWARE 列表从上到下执行,越靠前越早介入,但也越容易被后续中间件覆盖。
-
SecurityMiddleware必须在自定义中间件之前,否则 HTTPS 重定向可能干扰你的判断逻辑 - 如果用了
django.contrib.auth.middleware.AuthenticationMiddleware,它会设置request.user;你若在它之前做校验,request.user还是AnonymousUser,不能依赖这个对象 - 中间件类必须放在
settings.py的MIDDLEWARE列表中,且路径要完整(如"myapp.middleware.IPBlockMiddleware"),少一个点就静默失效 - 开发时关掉
DEBUG=True,否则某些异常会被调试页面吞掉,看起来像“没触发”
如何测试中间件是否真正拦截了请求?
别只看浏览器 403 页面,得确认请求根本没进视图、也没触发数据库查询。
立即学习“Python免费学习笔记(深入)”;
- 在目标视图函数开头加
print("view executed"),然后用curl -v http://localhost:8000/illegal-path/测试;没看到输出,说明中间件生效 - 用 Django Debug Toolbar 查看 SQL 查询数,拦截成功时应为 0
- 日志最可靠:在中间件里写
logger.warning(f"Blocked IP {ip} on {request.path}"),再查日志文件确认记录 - 注意:测试 POST 请求时,
request.body在中间件读取后会变空,后续视图拿不到数据;要用request.read()并缓存,或改用request.POST(已解析)


















