FastAPI 中间件须用 BaseHTTPMiddleware 重写 dispatch 方法改请求头,因 headers 只读;改响应体应避免直接操作 body,而用 JSONResponse 重建或包装 body_iterator;Django ASGI 下需用 call 替代 process_request。

FastAPI 中间件怎么写:用 BaseHTTPMiddleware 拦截请求头
直接改请求头必须在中间件里做,不能靠路由函数——因为请求头在进入路由前就解析完了。用 BaseHTTPMiddleware 是最可控的方式,它让你拿到原始 Request 对象,还能提前 abort 或 rewrite。
常见错误是试图在依赖(Depends)里改 request.headers,那只是个只读 ImmutableMultiDict,改了也白改;还有人用装饰器套路由,结果漏掉 404 或静态文件路径。
- 必须继承
BaseHTTPMiddleware,重写dispatch方法 -
request.headers是只读的,要改头得构造新Request,传入scope+ 新headers - 如果只是读取或校验(比如检查
X-Api-Key),直接用request.headers.get("X-Api-Key")即可,不用重建 - 注意异步:整个
dispatch必须是async def,且调用call_next(request)是 awaitable
Starlette 中间件怎么改响应体:别直接操作 Response 实例
想在返回前加字段、压缩 JSON、注入 trace ID?别碰 Response.body——它可能被流式生成(比如 StreamingResponse),也可能已被 gzip 编码过。正确做法是包装 Response 的 body_iterator 或重写 render 方法。
典型翻车现场:有人用 response.body = b'{"data":...}' 强行覆盖,结果遇到 HTMLResponse 或 RedirectResponse 就报 AttributeError;还有人试图 json.loads(response.body) 再 dump,却忘了 body 是 bytes 且可能为空。
- 对普通
JSONResponse,推荐在中间件里拦截后重新构造:JSONResponse(content={**original_content, "trace_id": tid}) - 若需修改任意响应体,用
Response的render方法(仅适用于已完全生成 body 的响应),或用StreamingResponse包装原始body_iterator - 不要假设所有响应都是 JSON:先判断
isinstance(response, JSONResponse),再处理 - 修改响应头(如加
X-Processed-At)安全得多,直接response.headers["X-Processed-At"] = str(time.time())
Django 中间件怎么兼容 ASGI 和 WSGI:避免 process_request 陷阱
Django 4+ 默认走 ASGI,但很多老中间件还写着 process_request,这方法在 ASGI 模式下根本不会被调用——它只在传统 WSGI 请求周期里生效。真要拦截请求头或改响应,得统一用 __call__ 或 __await__。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
一个容易被忽略的点:Django 的 get_response 在 ASGI 下返回的是协程,不是 Response 实例。如果你写了 response = get_response(request),在 ASGI 环境会卡住或报 RuntimeWarning: coroutine 'get_response' was never awaited。
- ASGI 兼容写法:中间件类必须实现
__call__,并在里面判断isinstance(get_response, collections.abc.Coroutine) - 更稳妥的做法是统一用
AsyncMiddlewareMixin,或直接写成异步类(Django 4.1+ 支持) - 修改请求头只能读,不能改;真正要改,得在
__call__里提前构造新HttpRequest(不推荐),或把逻辑下沉到视图层 - 响应头修改是安全的:
response["X-Frame-Options"] = "DENY"在 WSGI/ASGI 下都有效
中间件顺序为什么重要:AuthenticationMiddleware 必须在 CORSMiddleware 之后?
不是“必须”,而是取决于你想拦截什么。比如 CORSMiddleware 默认在最外层,它会先响应预检(OPTIONS)请求,根本不进后续中间件;而 AuthenticationMiddleware 需要读取 Authorization 头,如果被 CORS 挡在前面,token 就永远到不了认证层。
另一个坑是日志中间件放太靠后——比如放在所有业务中间件之后,那 500 错误可能根本没走到它就崩了,导致日志缺失。还有人把压缩中间件(如 GZipMiddleware)放错位置,导致压缩了已加密或已编码的响应体,前端解不开。
- 通用顺序建议(从外到内):
CORSMiddleware→AuthenticationMiddleware→LoggingMiddleware→CompressionMiddleware - 任何依赖请求头的中间件,不能放在会吞掉 headers 的中间件之后(比如某些反向代理中间件会清理
X-Forwarded-*) - 修改响应体的中间件,尽量靠近最内层(即紧挨着视图),否则可能被外层中间件覆盖或二次处理
- 调试时用
print(scope["path"], scope["method"])打点,确认中间件是否真的被执行
中间件最难的从来不是写法,而是厘清执行时机和数据所有权:请求头不可变、响应体可能流式、ASGI/WSGI 分支行为不一致——这些边界一旦模糊,问题就藏在偶发的 400 或空响应里,很难复现。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










