You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

FastAPI中如何使用中间件修改请求体?

FastAPI中如何使用中间件修改请求体?

嘿,这个问题我熟!在FastAPI里用中间件修改POST请求体确实有点小技巧,因为默认的Request对象的body是一次性读取且不可直接修改的,我给你一步步讲清楚怎么做。

首先得明确:FastAPI底层基于Starlette,请求体只能被读取一次,所以我们需要先把原始请求体读出来,修改后再重新构建一个新的请求对象传给后续的处理流程。

针对JSON格式的POST请求

这是最常见的场景,比如前端传JSON数据,我们要在中间件里修改它:

from fastapi import FastAPI, Request
import json
import asyncio

app = FastAPI()

@app.middleware("http")
async def modify_post_body(request: Request, call_next):
    # 只处理POST请求
    if request.method == "POST":
        try:
            # 读取原始请求体(bytes格式)
            raw_body = await request.body()
            # 解析成Python字典
            body_data = json.loads(raw_body.decode("utf-8"))
            
            # 这里就是你要做的修改操作啦,比如添加一个字段
            body_data["middleware_modified"] = True
            # 或者修改现有字段:body_data["username"] = body_data["username"].upper()
            
            # 把修改后的数据转回bytes格式
            modified_body = json.dumps(body_data).encode("utf-8")
            
            # 重新构建请求的scope和headers,因为请求体变了,Content-Length也要更新
            new_scope = dict(request.scope)
            new_headers = dict(request.headers)
            new_headers["content-length"] = str(len(modified_body))
            # 把headers转成Starlette要求的字节组格式
            new_scope["headers"] = [(k.encode(), v.encode()) for k, v in new_headers.items()]
            
            # 创建新的Request对象,让后续流程读取修改后的请求体
            async def receive_modified_body():
                return {"type": "http.request", "body": modified_body}
            
            modified_request = Request(new_scope, receive=receive_modified_body)
        except json.JSONDecodeError:
            # 如果请求体不是JSON格式,就用原始请求
            modified_request = request
    else:
        # 非POST请求直接跳过
        modified_request = request
    
    # 把修改后的请求传给后续路由处理
    response = await call_next(modified_request)
    return response

# 测试用的路由,看看修改后的请求体
@app.post("/test")
async def test_route(data: dict):
    return {"received_data": data}

针对表单格式的POST请求

如果你的请求是application/x-www-form-urlencoded格式的表单数据,修改方式类似,只是解析和编码的方法不同:

from urllib.parse import parse_qs, urlencode

@app.middleware("http")
async def modify_form_body(request: Request, call_next):
    if request.method == "POST" and request.headers.get("content-type") == "application/x-www-form-urlencoded":
        try:
            raw_body = await request.body()
            body_str = raw_body.decode("utf-8")
            # 解析表单数据成字典
            form_data = parse_qs(body_str)
            
            # 修改表单数据,比如添加一个字段
            form_data["new_field"] = ["modified_value"]
            
            # 把修改后的数据转回表单字符串
            modified_body_str = urlencode(form_data, doseq=True)
            modified_body = modified_body_str.encode("utf-8")
            
            # 同样更新headers和构建新请求
            new_scope = dict(request.scope)
            new_headers = dict(request.headers)
            new_headers["content-length"] = str(len(modified_body))
            new_scope["headers"] = [(k.encode(), v.encode()) for k, v in new_headers.items()]
            
            async def receive_modified_body():
                return {"type": "http.request", "body": modified_body}
            
            modified_request = Request(new_scope, receive=receive_modified_body)
        except Exception:
            modified_request = request
    else:
        modified_request = request
    
    response = await call_next(modified_request)
    return response

小提醒

  • 如果你只想修改特定路径的POST请求,可以在中间件里加个路径判断,比如if request.url.path == "/api/submit"
  • 处理异常很重要,避免因为请求格式不符合预期导致整个服务崩溃。
  • 如果你需要处理多种格式的请求体,可以在中间件里根据Content-Type头做分支处理。

备注:内容来源于stack exchange,提问作者tamirg

火山引擎 最新活动