FastAPI 中如何通过中间件修改请求体(Request Body)

在 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"}

⚠️ 重要注意事项:

  • 顺序敏感:app.add_middleware() 必须在所有 @app.post() 等路由注册之前调用,否则中间件不会生效;
  • 分块支持:真实场景中请求体可能被拆分为多个 http.request 消息(含 more_body=True),上述示例仅处理单块情形;如需完整支持流式请求,需缓存并拼接全部分块;
  • 类型安全:修改前建议校验 Content-Type: application/json 头,避免误操作表单或二进制数据;
  • 性能影响:每次请求均触发 JSON 解析与序列化,高频场景下应评估开销,必要时加入条件判断(如仅匹配特定路径);
  • 替代方案思考:若仅需预处理参数,优先考虑依赖注入(Depends)或 BaseModel 的 @root_validator —— 中间件修改应限于跨域、审计、协议转换等通用需求。

通过 ASGI 中间件精准控制请求流入,是 FastAPI 实现深度定制化请求处理的核心能力之一。