process_request 是 Django 4.2 中最直接的请求拦截入口,它在视图执行前运行,返回 HttpResponse 即可中断流程;需显式声明 get_response 参数,避免在中间件中同步查数据库,应使用内存缓存或 Redis 实现高效黑名单校验。

Middleware 中如何拦截请求并拒绝访问
在 Django 4.2 中,process_request 是最直接的拦截入口——它在视图执行前运行,返回 HttpResponse 即可中断流程。不要用 process_view 或 process_response 做封禁,前者依赖视图解析(可能已触发副作用),后者太晚(响应已生成)。
常见错误是把逻辑写在 process_response 里,结果封禁失效还报 ResponseNotReady 错误;另一个坑是忘记在中间件类中显式声明 get_response 参数(Django 4.2 要求构造函数必须接收它)。
示例结构:
class BlacklistMiddleware:
def __init__(self, get_response):
self.get_response = get_response
<pre class="brush:php;toolbar:false;">def __call__(self, request):
if self.is_blocked(request):
from django.http import HttpResponseForbidden
return HttpResponseForbidden("Access denied")
return self.get_response(request)
def is_blocked(self, request):
# 实现你的判断逻辑
return False
立即学习“Python免费学习笔记(深入)”;
如何高效判断 IP、User-Agent 或 Referer 是否在黑名单中
硬编码列表只适合临时调试;生产环境必须用可热更新、低延迟的数据结构。推荐两种方案:
- 内存缓存 + 定时刷新:用
django.core.cache.cache存储set类型的 IP 黑名单,配合后台任务(如 Celery 或系统 cron)定期从数据库或 Redis 同步最新数据 - Redis 原生支持:用
redis.Redis.sismember("blacklist:ip", request.META.get("REMOTE_ADDR")),比查 DB 快一个数量级,且天然支持分布式部署
注意:request.META.get("HTTP_USER_AGENT") 和 request.META.get("HTTP_REFERER") 都可能为 None,需判空;IP 还要考虑代理场景,REMOTE_ADDR 可能被伪造,建议结合 HTTP_X_FORWARDED_FOR(但要验证可信代理链)。
为什么不能直接在 Middleware 里调用 Model 查询
每次请求都查一次数据库,QPS 上百就会拖垮 DB 连接池,尤其在高并发下容易触发 django.db.utils.OperationalError: too many connections。Django 的 ORM 默认不支持异步,中间件同步阻塞会卡住整个请求线程。
正确做法是预加载+缓存:
- 启动时(如
AppConfig.ready())一次性加载黑名单到内存set或dict - 搭配信号(
post_saveonBlacklistEntry)自动更新内存副本 - 或改用
cache.get_or_set("blacklist:ips", load_from_db, timeout=300),避免缓存雪崩
别信“小项目无所谓”——哪怕只有几十 QPS,未加锁的缓存击穿也可能让 DB 瞬间 CPU 拉满。
如何让封禁对爬虫和 API 请求同样生效
默认中间件对所有请求一视同仁,但要注意:API 接口(如 DRF)通常返回 JSON,而 HttpResponseForbidden 返回 HTML 文本,前端或爬虫可能无法正确识别状态码含义。
建议统一返回标准格式:
- 对
application/json请求头,返回JsonResponse({"detail": "Forbidden"}, status=403) - 对普通请求,仍用
HttpResponseForbidden - 额外检查
request.path.startswith("/api/")或通过 URL pattern 判断是否为 API 流量
别漏掉静态资源路径(如 /static/、/media/)——Django 开发模式下它们由中间件处理,线上 Nginx 通常直出,所以封禁逻辑是否覆盖这些路径,得看你的部署架构。
真正难的不是写几行封禁代码,而是保证它不拖慢正常请求、不因缓存不一致放行恶意流量、也不在多进程/多线程下读到脏数据。上线前务必压测中间件路径,观察 Redis 连接数和缓存命中率。


















