FastAPI使用starlette-context插件如何获取request.body内容?
问题根因
- 第一个报错的核心原因是你中间件配置中的
plugins参数没有正确声明为元组:Python语法中单个元素的括号包裹内容默认识别为单个对象,只有加后缀逗号才会被判定为元组,starlette-context在遍历插件列表时没有正确执行该异步插件方法,导致request.body()协程未被调用,抛出警告。 - 第二个路由无法接收请求的问题,是因为Starlette的请求体是流式结构,只能消费一次,你在插件中调用
await request.body()之后,流已经读取到末尾,后续FastAPI路由层再读取请求体时会拿到空值。
修正方案
1. 自定义插件改造
读完请求体后将内容重新写入请求的流对象,保证后续链路可以正常读取:
from typing import Any, Optional, Union import io from starlette.requests import HTTPConnection, Request from starlette_context.plugins import Plugin class BodyPlugin(Plugin): key = "body" async def process_request( self, request: Union[Request, HTTPConnection] ) -> Optional[Any]: # 仅处理普通HTTP请求,跳过WebSocket等其他连接类型 if not isinstance(request, Request): return None body = await request.body() # 重置请求流,让后续链路可以再次读取请求体 request._stream = io.BytesIO(body) return body
2. 中间件配置修正
给plugins元组加后缀逗号,保证类型正确:
middleware = [ Middleware( ContextMiddleware, plugins=( BodyPlugin(), # 单个元素必须加逗号,才会被识别为元组 ) ) ]
注意事项
- 大文件上传、流式请求的场景不建议全局采集请求体,会占用大量内存,可通过
request.headers.get("content-type")判断请求类型后跳过采集。 - 如果请求体是压缩格式(gzip/deflate),需要先解压再存储,否则采集到的是二进制压缩内容。
内容的提问来源于stack exchange,提问作者Xing Liu
相关产品推荐
相关产品推荐

