FastAPI 请求体(Request Body)完整指南:用 Pydantic 模型声明、验证与自动生成文档
本篇指南基于 FastAPI 官方文档中的 Request Body 教程,讲清楚一件事:当客户端(如浏览器、curl、前端应用)需要向你的 API 发送数据时,如何用 Pydantic 模型声明 Request Body,让 FastAPI 自动完成 JSON 读取、类型转换、数据验证、OpenAPI 文档生成和编辑器补全。读完本文,你将掌握:如何用 BaseModel 定义请求体模型、如何在路径操作中直接以类型注解声明请求体、FastAPI 如何自动区分 Body/路径/Query 参数,以及这套机制在 FastAPI 源码中的实现位置。
1. 什么是 Request Body,它与 Response Body 有何区别
Request Body 是客户端发送到你的 API 的数据;Response Body 是你的 API 发送给客户端的数据。你的 API 几乎总要发送 Response Body,但客户端并不总是发送 Request Body——很多时候客户端只是请求某个路径,最多附带几个 Query 参数,但没有 Body。
在 FastAPI 中声明 Request Body 的方式是:使用 Pydantic 模型,并享受 Pydantic 提供的所有能力与优势。
注意(HTTP 方法选择):
- 若要发送数据,建议使用
POST(最常用)、PUT、DELETE或PATCH之一;- 在
GET请求中发送 Body 在 HTTP 规范中是未定义的行为。FastAPI 虽然仍支持这么做(仅限非常复杂/极端的场景),但不推荐。因此 Swagger UI 交互文档在使用GET时不会显示 Body 的文档,并且一些中间代理(Proxy)也可能不支持。
2. 第一步:导入 Pydantic 的 BaseModel
首先需要从 pydantic 导入 BaseModel,完整入口文件见 tutorial001_py310.py:
from fastapi import FastAPI
from pydantic import BaseModel
3. 创建你的数据模型
将数据模型声明为一个继承自 BaseModel 的类,所有属性使用标准 Python 类型:
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
这里有两个关键约定:
- 有默认值的属性不是必填的,没有默认值则是必填的;使用
None可以让属性变为可选(optional); - 这与声明 Query 参数的规则一致:默认值决定必填性,类型注解决定校验与补全。
以上面这个模型为例,它声明了一个 JSON "object"(即 Python dict),例如:
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
由于 description 和 tax 是可选的(默认值为 None),下面这个 JSON object 同样合法:
{
"name": "Foo",
"price": 45.2
}
4. 将模型声明为路径操作的参数
把模型加入路径操作(path operation)的方式,与声明路径参数、Query 参数完全一样——直接写类型注解:
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
参数 item 的类型注解声明为你创建的模型 Item,仅此一行代码,FastAPI 就会将其识别为 Request Body。
5. 仅凭类型声明,FastAPI 自动完成的事情
有了这一行 Python 类型声明后,FastAPI 会:
- 将 Request Body 作为 JSON 读取;
- 转换相应的类型(在需要时);
- 验证这些数据:
- 若数据无效,返回一个清晰可读的错误,精确指出在哪里、哪些数据不正确;
- 把收到的数据以
item参数传递给你:- 由于你在函数中将其声明为
Item类型,你会获得完整的编辑器支持(自动补全等),覆盖所有属性及其类型;
- 由于你在函数中将其声明为
- 为你的模型生成 JSON Schema 定义,你也可以在项目的其他地方复用这些 Schema(如果对你有意义的话);
- 这些 Schema 会成为生成的 OpenAPI Schema 的一部分,并被自动文档 UI(Swagger UI、ReDoc 等)使用。
源码级印证:请求体是如何被识别和读取的
从源码结构看,上述"自动识别"发生在依赖解析阶段。fastapi/dependencies/utils.py 中,当参数没有显式的 Annotated 字段信息时,FastAPI 按以下顺序推断参数类型:
if is_path_param:
field_info = params.Path(annotation=use_annotation)
elif is_uploadfile_or_nonable_uploadfile_annotation(...) or ...:
field_info = params.File(annotation=use_annotation, default=default_value)
elif not field_annotation_is_scalar(annotation=type_annotation):
field_info = params.Body(annotation=use_annotation, default=default_value)
else:
field_info = params.Query(annotation=use_annotation, default=default_value)
也就是说:与路径模板匹配的参数成为路径参数;非标量类型(如 Pydantic 模型)被推断为 Body;标量类型则被推断为 Query。请求到达时,request_body_to_args(fastapi/dependencies/utils.py)负责真正读取请求体、按模型字段验证并构造出传给端点函数的参数。这一实现与文档中"FastAPI 会正确识别每个参数并取数于正确的位置"的描述完全对应。
6. 自动文档:JSON Schema 进入 OpenAPI 与 Swagger UI
你的模型的 JSON Schema 会成为 OpenAPI 生成 Schema 的一部分,并显示在交互式 API 文档中(Schema 组件定义):
并且在每个需要它的路径操作中也会被直接使用(左侧为操作定义、右侧为可编辑的请求体示例):
7. 编辑器支持:类型提示与错误检测
在编辑器中,你在函数内部能获得类型提示和代码自动补全(如果你拿到的是一个 dict 而不是 Pydantic 模型,就不会有这些):
对于错误的类型操作,你还会收到错误提示:
这不是偶然——整个框架就是围绕这一设计构建的。这个设计在进入实现之前就在设计阶段经过了充分测试,以确保它对所有编辑器都有效;甚至 Pydantic 本身也为此做过一些修改。
提示:如果你使用 PyCharm,可以使用 Pydantic PyCharm 插件(Pydantic 社区插件),它针对 Pydantic 模型提供:代码补全、类型检查、重构、搜索、代码检查(inspections)等增强的编辑器支持。在 Visual Studio Code、PyCharm 和大多数 Python 编辑器中,你都能获得同样的编辑器支持。
8. 在操作函数中使用模型
在函数内部,你可以直接使用模型对象的所有属性。见 tutorial002_py310.py:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.model_dump()
if item.tax is not None:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
注意示例中直接访问 item.tax、item.price——它们是强类型的模型属性;item.model_dump() 是 Pydantic v2 中将模型转为 dict 的标准方式(旧版 Pydantic v1 中对应 item.dict())。对应的行为验证见 tests/test_tutorial/test_body/test_tutorial002.py。
9. Request Body + 路径参数:同时声明
你可以同时声明路径参数和 Request Body。FastAPI 会识别出:与路径参数同名的函数参数取自路径,而类型为 Pydantic 模型的函数参数取自 Request Body。见 tutorial003_py310.py:
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.model_dump()}
这里 item_id: int 取自 URL 路径 {item_id},item: Item 取自请求体 JSON。行为验证见 tests/test_tutorial/test_body/test_tutorial003.py。
10. Request Body + 路径参数 + Query 参数:三者共存
你也可以在同一操作中同时声明 Body、路径 和 Query 参数,FastAPI 会把每一处数据都从正确的位置取出来。见 tutorial004_py310.py:
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.model_dump()}
if q:
result.update({"q": q})
return result
函数参数按以下规则识别:
| 规则 | 结果 |
|---|---|
参数同时出现在 路径模板中(如 {item_id}) |
作为路径参数使用 |
参数是简单类型(int、float、str、bool 等) |
作为 Query 参数 |
| 参数是 Pydantic 模型类型 | 作为 Request Body |
这条识别规则与 fastapi/dependencies/utils.py 中 is_path_param → 非 field_annotation_is_scalar → params.Body / params.Query 的推断逻辑一一对应。
注意:FastAPI 之所以知道
q不是必填的,是因为它有默认值= None。FastAPI 不会通过str | None这个联合类型来判断参数是否必填。但添加str | None类型注解可以让编辑器提供更好的支持并检测错误。
11. 不使用 Pydantic 模型:多个简单 Body 参数
如果你不想使用 Pydantic 模型,也可以直接使用 Body 参数声明多个简单类型的请求体字段。详见《Body – 多个参数:Body 中的简单值》(仓库路径:docs/de/docs/tutorial/body-multiple-params.md)。
从源码看,多个 Body 参数最终会经由 create_body_model(fastapi/dependencies/utils.py)在运行时动态合并为一个 Pydantic 模型再统一验证——也就是说,即使你不写模型类,FastAPI 底层依然在用一个 Pydantic 模型承接整个请求体。
小结与适用前提
- 声明 Request Body 的完整链路:
from pydantic import BaseModel→ 继承BaseModel定义模型 → 在路径操作函数中用模型类型注解一个参数; - 发送数据优先使用
POST/PUT/DELETE/PATCH,避免GET携带 Body; - 参数来源(Body / 路径 / Query)完全由类型 + 路径模板 + 默认值决定,规则在 fastapi/dependencies/utils.py 中实现;
- 上述示例代码(如
str | None联合类型写法)要求 Python 3.10+,这与docs_src/body/tutorial00*_py310.py文件名中的py310标记一致; - 教程代码全部可在 docs_src/body/ 中查看,对应测试位于 tests/test_tutorial/test_body/,可运行 pytest 验证行为。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00



