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




