中间件类必须继承 BaseMiddleware 或实现 call 方法;Django 2.0+ 要求中间件为可调用对象,推荐继承 MiddlewareMixin 以兼容新旧钩子,否则不会被调用。

中间件类必须继承 BaseMiddleware 或实现 __call__ 方法
Django 2.0+ 强制要求中间件是可调用对象,最稳妥写法是继承 django.utils.deprecation.MiddlewareMixin(它已帮你封装了 __call__ 和兼容旧式 process_request 等钩子)。不继承、也不手动实现 __call__,中间件根本不会被调用。
- 老式写法(Django process_request 单独存在时,在新版中会被静默忽略
- 如果你手动写了
__call__,必须显式调用self.get_response(request),否则请求就卡住了,不会继续往下走 - 别在
__init__里做耗时操作——它在 Django 启动时执行一次,不是每次请求都调用
process_request 和 process_response 的触发时机与返回值含义
这两个方法只在你用了 MiddlewareMixin 时才有效;它们不是装饰器,而是生命周期钩子:前者在视图函数执行前运行,后者在视图返回 HttpResponse 对象后、发给客户端前运行。
-
process_request返回None表示放行;返回任意HttpResponse子类实例(如HttpResponseRedirect),则直接终止后续中间件和视图,立刻响应 -
process_response必须返回一个HttpResponse对象(哪怕只是原样返回response),否则会抛出TypeError: object of type 'NoneType' is not callable - 注意:如果上游中间件在
process_request中返回了响应,那你的process_response依然会被调用——Django 保证每个成功进入的请求,都会配对调用对应中间件的process_response
如何安全地修改请求或响应体(比如加 header、改 body)
请求对象 request 是只读的(request.body 是 bytes,且可能已被读取过);响应对象 response 可以直接赋值属性,但改 response.content 需小心编码和 Content-Length 头。
- 往响应加 header:直接
response['X-Trace-ID'] = 'abc123'即可,Django 会自动处理重复 key 和编码 - 想读原始请求 body?用
request.body,但注意它只能读一次;如果视图或其它中间件已经调用过request.POST或json.loads(request.body),再读就是空 bytes - 修改响应 body:先确保
response.streaming == False,然后response.content = new_bytes,再手动重算response['Content-Length'] = str(len(new_bytes)),否则浏览器可能卡在加载
调试中间件顺序和是否生效的三个实操办法
Django 按 MIDDLEWARE 配置列表顺序执行中间件,顺序错了会导致拦截失效、header 被覆盖、甚至循环重定向。
立即学习“Python免费学习笔记(深入)”;
- 在中间件方法开头加
print(f"[{self.__class__.__name__}] enter"),启动开发服务器看控制台输出顺序 - 检查
settings.py里的MIDDLEWARE列表:认证类(如AuthenticationMiddleware)必须在依赖用户信息的中间件之前;CSRF 中间件要靠近视图层,太靠前可能拦截掉健康检查接口 - 用
curl -I http://localhost:8000/xxx看响应头是否出现你加的字段;如果没出现,要么中间件没注册,要么被后面的中间件覆盖(比如两个中间件都设response['Cache-Control'])
return response,或错放一个 process_exception 而没处理异常类型,整个请求流就断了。最常被忽略的是:你以为只拦截 API,结果静态文件(/static/)也进了你的中间件,而你又没判断 request.path.startswith('/static/'),导致 JS/CSS 返回 500。


















