FastAPI使用中间件与依赖注入记录请求时代码阻塞问题求解
FastAPI中间件搭配依赖注入阻塞问题解决方案
问题根因
这是FastAPI 0.66.0以下版本搭配对应旧版Starlette时的已知缺陷:
OAuth2PasswordRequestForm依赖会主动读取请求的表单格式请求体,旧版框架中请求体被依赖读取后,中间件的事件循环处理逻辑出现上下文挂起,导致await call_next(request)之后的代码不会立即执行,响应也不会立即返回,直到下一个请求触发事件循环的队列调度才会处理上一个请求的剩余逻辑。- 无依赖的接口因为没有提前读取请求体,所以不会触发该缺陷。
解决方案
方案1(优先推荐):升级框架版本
直接将FastAPI升级到0.66.0及以上的稳定版,同时匹配升级Starlette到对应兼容版本,官方已经在后续版本修复了该请求体读取导致的事件循环挂起问题,升级后无需修改原有代码即可恢复正常。
方案2(无法升级版本时临时修复):中间件提前缓冲请求体
如果项目受依赖限制无法升级框架,可以在中间件中主动读取并缓冲请求体,再重新挂载到请求对象上,避免依赖读取请求体后触发的挂起问题,修改后的中间件代码示例:
@app.middleware("http") async def log_request(request: Request, call_next): # 提前读取并缓冲请求体 req_body = await request.body() # 原有入站请求日志逻辑 print(f"[日志] 入站请求:{req_body=} ...") # 将缓冲的请求体重新挂载到request对象,确保后续依赖可以正常读取 request._body = req_body response = await call_next(request) # 原有出站响应日志逻辑 print(f"[日志] 出站响应:{response.status_code=} ...") return response
方案3:改用自定义路由实现日志逻辑
如果不想修改中间件的请求体处理逻辑,可以放弃使用HTTP中间件,改为继承FastAPI的APIRoute自定义路由类实现全局日志逻辑,该方案不会和依赖的请求体读取逻辑产生冲突,兼容性更高。
内容的提问来源于stack exchange,提问作者Alexey Yurchenko
相关产品推荐
相关产品推荐

