推荐用Pydantic模型接收JSON body:继承BaseModel,FastAPI自动解析校验;错误做法是仅用dict注解;数组需声明list[Item];字段名须与JSON key完全一致;裸字符串需Body(..., media_type="text/plain")或embed=True。

用 Pydantic 模型接收 JSON body 是最推荐的方式
FastAPI 默认期望 POST 请求的 Content-Type 为 application/json,此时直接用继承自 BaseModel 的类作为参数即可自动解析、校验、类型转换。这是最安全、最可维护的做法。
常见错误现象:不定义模型,直接写 def api(data: dict) —— FastAPI 不会做任何解析或校验,data 会是空字典或报 value is not a valid dict 错误。
- 必须从
pydantic导入BaseModel,不能只靠 Python 类型注解 - 字段设为
Optional[str] = None表示可选;不设默认值(如title: str)表示必填 - 如果客户端发的是裸 JSON 数组(如
[{"id":1}]),需用list[Item]声明参数类型,而非Item - 模型字段名必须与 JSON key 完全一致(区分大小写),否则字段为
None或触发校验失败
不依赖模型时,用 Body() 获取原始 JSON 字节再手动解析
当你无法控制客户端发什么(比如旧系统发 application/x-www-form-urlencoded 但 body 里塞了 JSON 字符串),或需要统一预处理逻辑,就得绕过自动解析,自己读 raw body。
注意:直接用 await request.body() 返回 bytes,不是 str;且不能和 Form、Body 混用在同一接口里。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 先声明
body_raw = Body(...),它在application/json下是dict,在application/x-www-form-urlencoded下是bytes - 若为
bytes,需用json.loads(body_raw.decode())手动转成dict - 别用
request.json()—— FastAPI 已移除该方法,调用会报错 - 这种写法失去自动校验和文档生成能力,仅建议用于临时兼容场景
Body(embed=True) 和裸字符串提交的适配要点
当客户端发的是单个字符串(如 "hello")而非对象(如 {"q": "hello"}),默认的 Body() 会失败,因为 FastAPI 试图把它当 JSON object 解析。
此时有两个选择:
- 前端改用对象包装:
{"info": "hello"},后端用info: str = Body(..., embed=True) - 后端保持接收裸字符串:改用
Body(..., media_type="text/plain"),再配合await request.body()读取并解码 -
embed=True只影响解析结构,不影响 Content-Type 校验;若前端发text/plain却用embed=True,仍会 422
容易被忽略的边界情况
很多问题出在「以为发的是 JSON,其实不是」。比如 Lua 脚本用 http.post 发请求,没显式设 headers["Content-Type"] = "application/json",默认就是 application/x-www-form-urlencoded,导致 FastAPI 尝试用 Form 解析却收到 JSON 字符串,直接抛 ('body',): value is not a valid dict。
真正健壮的接口,得在日志里打一行 request.headers.get("content-type") 和 await request.body() 前十字符,否则排查时永远在猜。

















