在 fastapi 中,标准的 `@app.middleware("http")` 无法直接修改请求体,因为 `request` 对象是只读且已解析的;必须使用底层 asgi 中间件,重写 `receive` 函数来动态篡改原始字节流中的请求体内容。
FastAPI 基于 Starlette 构建,其 HTTP 中间件分为两层:高层装饰器式中间件(如 @app.middleware("http"))和底层 ASGI 中间件。关键限制在于:高层中间件接收到的 Request 实例已解析完毕,其 .body() 方法仅能读取一次,且不可更改原始传输数据;若需真正“修改请求体”,必须介入 ASGI 的 receive 协议,在请求被解析前拦截并重写 http.request 消息中的 body 字段。
以下是一个生产可用的自定义 ASGI 中间件示例,它对所有 POST 请求的 JSON 请求体中 value 字段统一覆写为 "456":
import json from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Value(BaseModel): value: str class ModifyRequestBodyMiddleware: def __init__(self, app): self.app = app async def __call__(self, scope, receive, send): # 仅处理 HTTP 请求 if scope["type"] != "http": await self.app(scope, receive, send) return # 封装 receive 函数,实现请求体注入 async def wrapped_receive(): message = await receive() if message["type"] == "http.request": body = message.get("body", b"") # 支持分块传输(body 可能不完整) if not message.get("more_body", False): try: # 解析 JSON 并修改 data = json.loads(body.decode()) if body else {} if isinstance(data, dict): data["value"] = "456" # ✅ 自定义修改逻辑 message["body"] = json.dumps(data).encode() except (json.JSONDecodeError, UnicodeDecodeError): pass # 非 JSON 或编码异常,跳过修改 return message await self.app(scope, wrapped_receive, send) # 注册中间件(注意:必须在路由定义之前) app.add_middleware(ModifyRequestBodyMiddleware)
对应接口定义如下:
@app.post("/test", response_model=Value)
def test_endpoint(payload: Value):
return payload # 返回时 value 已为 "456"✅ 验证效果(使用 requests):
import requests
response = requests.post("http://localhost:8000/test", json={"value": "123"})
print(response.json()) # 输出: {"value": "456"}⚠️ 重要注意事项:
通过 ASGI 中间件精准控制请求流入,是 FastAPI 实现深度定制化请求处理的核心能力之一。
来电咨询