首页
/ FastAPI 请求体(Request Body)详解:基于 Pydantic 模型的声明、校验与自动文档实战

FastAPI 请求体(Request Body)详解:基于 Pydantic 模型的声明、校验与自动文档实战

2026-09-07 10:59:54作者:滑思眉Philip

导读

本篇技术指南以 FastAPI 官方教程中「Request Body(请求体)」章节为骨架,完整讲解如何在 FastAPI 中通过 Pydantic 模型声明、接收并校验客户端提交的请求体数据,并剖析其在当前仓库源码中的解析与 OpenAPI 生成原理。读完本文你将掌握:如何用几行 Python 类型标注完成请求体的 JSON 解析、类型转换、数据校验与编辑器智能提示,如何将请求体与路径参数、查询参数混合使用,以及请求体校验失败时 FastAPI 返回标准化 422 错误的底层机制。

什么是请求体(Request Body)

当你需要从客户端(例如一个浏览器或移动 App)向 API 发送数据时,这些数据通常以 request body(请求体) 的形式传递。与之对应,API 返回给客户端的数据称为 response body(响应体)

  • 请求体(request body):客户端发送给 API 的数据;
  • 响应体(response body):API 返回给客户端的数据。

API 几乎总是需要返回响应体,但客户端并非每次都要发送请求体——有时只访问一个路径,可能附带几个查询参数(query),却不发送任何 body。

注意:要发送请求体数据,应使用以下 HTTP 方法之一:POST(最常见)、PUTDELETEPATCH。HTTP 规范并未定义在 GET 请求中发送 body 的行为,尽管 FastAPI 出于极少数复杂/极端场景仍予以支持,但由于官方并不推荐,Swagger UI 交互式文档在 GET 请求下不会展示该 body 的文档,且中间的代理服务器也可能不支持这种用法。

在 FastAPI 中声明请求体,依靠的是 Pydantic 模型及其全部能力。仓库中对应的官方教程源码位于 docs_src/body/,本文后续所有示例均可在该目录下找到可运行的 tutorial00x_py310.py 文件。

第一步:从 Pydantic 导入 BaseModel

要声明请求体,首先从 pydantic 导入 BaseModel

from fastapi import FastAPI
from pydantic import BaseModel

源码出处:docs_src/body/tutorial001_py310.py

第二步:创建你的数据模型

然后,将你的数据模型声明为一个继承 BaseModel 的类,类的所有属性均使用 Python 标准类型标注。下面的 Item 模型覆盖了 strstr | Nonefloatfloat | None 四类字段:

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

与声明查询参数时的规则一致:模型属性带有默认值时该字段为可选项,否则为必填项;将默认值设为 None 即可让字段变成纯可选

例如,上面的模型声明了一个 JSON "object"(Python 中的 dict),形如:

{
    "name": "Foo",
    "description": "An optional description",
    "price": 45.2,
    "tax": 3.5
}

由于 descriptiontax 是可选的(默认值为 None),下面的 JSON 同样是合法请求体:

{
    "name": "Foo",
    "price": 45.2
}

这种"默认值决定必填性"的设计最终会反映到自动生成的 OpenAPI Schema 中。仓库测试 tests/test_tutorial/test_body/test_tutorial001.pyItem 生成的 JSON Schema 快照显示,required 数组中只有 ["name", "price"],而 descriptiontax 被建模为 anyOf 中允许 null 的可选属性。

第三步:把模型声明为路径操作函数的参数

将数据模型加入 path operation,方法与声明路径参数、查询参数完全一致——直接在函数签名中声明参数,并标注类型为刚刚创建好的模型类 Item

app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

完整源码见 docs_src/body/tutorial001_py310.py。这里的 async def 也可换成普通的 def,FastAPI 对两者都支持。

一次类型声明,FastAPI 自动完成的七件事

仅凭上面这一个 Python 类型标注,FastAPI 就会自动为你完成以下工作:

  1. 读取请求体并将其解析为 JSON;
  2. 类型转换(必要时),例如把 JSON 字符串 "50.5" 转换成 float
  3. 校验数据,若数据非法,返回清晰明确的错误,精确指出出错位置与原因;
  4. 把接收到的数据注入 item 参数。由于参数被标注为 Item 类型,编辑器会对该对象的所有属性及其类型提供完整补全(autocompletion)支持——这是直接接收 dict 无法获得的体验;
  5. 为模型生成 JSON Schema,可在项目其他有意义的场景复用;
  6. 这些 Schema 会并入自动生成的 OpenAPI schema
  7. OpenAPI schema 又被 Swagger UI 等自动文档 UI 使用,从而免去手写接口文档的负担。

自动文档:JSON Schema 如何呈现给客户端

模型生成 JSON Schema 后,会在 /docs 的交互式 API 文档中直观呈现。下图为数据模型部分对 Item(含必填标记 name*price*)以及自动生成的 ValidationErrorHTTPValidationError 错误模型的可视化展示:

FastAPI 交互式文档中自动生成的 Item 数据模型 JSON Schema,含必填字段与错误模型

同时,每个使用该模型的 path operation 的文档中也会显示对应的请求体区域,包括 required 标识、application/json 媒体类型以及可直接填写的示例值:

POST /items/ 接口文档中展示的 Request body 必填区域与 application/json 示例值

这两张截图对应的原始引用位于 docs/en/docs/tutorial/body.md 的自动文档章节,实际文档图片存放在 docs/en/docs/img/tutorial/body/。从 OpenAPI 层面看,仓库测试 tests/test_tutorial/test_body/test_tutorial001.py/openapi.json 返回的 schema 做了完整快照断言:请求体的 requestBody 被标记为 "required": True,其内容类型为 application/json,schema 通过 $ref 指向 #/components/schemas/Item——这正是 Swagger UI 渲染出上图界面的数据来源。

编辑器支持与类型安全

在函数体内,由于参数被标注为 Item 模型类型,编辑器会提供全量的类型标注与自动补全;若你对属性执行了错误类型的操作(例如对 str 调用数值运算),编辑器会直接给出错误提示。这种开发体验并非偶然——FastAPI 整个框架正是围绕 Python 类型标注这一设计原则构建的:在正式实现之前,团队曾在设计阶段进行过严格验证,以确保类型系统能与各类主流编辑器协同工作,甚至为支持这一设计对 Pydantic 做出过相应调整。上述截图均来自 Visual Studio Code,但 PyCharm 及绝大多数主流 Python 编辑器都能获得同样的支持。若使用 PyCharm,还可以搭配 Pydantic PyCharm 插件,获得针对 Pydantic 模型的自动补全、类型检查、重构、搜索与代码检查等增强能力。

在函数体内使用模型:以含税价格计算为例

一旦 FastAPI 将请求体注入参数,就可以像操作普通 Python 对象一样直接访问模型的每个属性。下面的 tutorial002 演示了如何计算含税价格:先用 model_dump() 把模型转为字典,再在 tax 存在时追加 price_with_tax 字段并返回:

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

完整源码见 docs_src/body/tutorial002_py310.py。注意:本例使用 Pydantic v2 风格的方法 item.model_dump() 将模型序列化为字典;返回的字典会被 FastAPI 自动转换为 JSON 响应。仓库中对应测试为 tests/test_tutorial/test_body/test_tutorial002.py

请求体 + 路径参数:同时声明

你可以同时声明路径参数与请求体。FastAPI 会智能识别:与路径(path)匹配的函数参数从 URL 路径中取值,声明为 Pydantic 模型的参数则从请求体中取值

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.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.model_dump()}

源码见 docs_src/body/tutorial003_py310.py。该接口的 OpenAPI 文档同时生成了 item_id 路径参数和 Item 请求体;仓库测试 tests/test_tutorial/test_body/test_tutorial003.py 验证了 PUT /items/123 携带完整 JSON 时返回 item_id 与模型字段合并的结果,也验证了只提交必填字段、或提交空对象 {} 时分别返回 200 与 422 的行为。

请求体 + 路径参数 + 查询参数:三合一

更进一步,你可以同时声明 body、path、query 三种参数,FastAPI 会分别识别并各自从正确的位置取值:

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.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

源码见 docs_src/body/tutorial004_py310.py。函数参数的具体识别规则如下:

  • 若参数同时出现在 path 中,则作为路径参数使用;
  • 若参数是单一类型(如 intfloatstrbool 等),则解释为查询参数
  • 若参数声明为 Pydantic 模型类型,则解释为请求体

可选性判定:默认值优先于类型标注

在混合参数的例子中,FastAPI 之所以知道 q 不是必填的,是因为它带有默认值 = None。这里有一个容易混淆的细节:str | None 这个联合类型标注本身并不会让 FastAPI 认为参数可选,参数是否为必填只取决于它是否有默认值。不过加上完整的类型标注仍非常值得——它能让你使用的编辑器提供更好的智能提示并及早发现类型错误。

底层原理:请求体在 FastAPI 中如何被解析

理解了"如何用",再看"为何如此"。在仓库源码中,请求体的处理链路清晰可循:

  • 参数分类:FastAPI 在 fastapi/dependencies/utils.py 中依据参数来源将其归类。源码函数 request_body_to_args()(见 fastapi/dependencies/utils.py)专门负责把解析后的请求数据映射回 Pydantic 模型字段,并据此生成校验错误。
  • 是否嵌入 body 的判定:函数 _should_embed_body_fields()(见 fastapi/dependencies/utils.py)与 _get_body_field()(见 fastapi/dependencies/utils.py)决定当函数只有一个 body 模型时,是把整个 JSON 对象直接交给该模型(这正是本文所有示例的行为),还是需要额外包裹一层——后者对应多 body 参数的"嵌入式"场景。
  • Body 参数的底层默认值:FastAPI 在 fastapi/params.py 中定义了 Body 类,其默认 media_typeapplication/json,并支持 embedaliasgt/ge/lt/lemin_length/max_length/pattern 等约束参数。当请求的 Content-Type 不是 JSON(如发送表单编码数据)时,校验将失败——这一点被测试 tests/test_tutorial/test_body/test_tutorial001.py 覆盖。
  • OpenAPI 生成fastapi/openapi/utils.py 依据 body_field 及其字段信息生成 requestBody 的媒体类型、schema 引用与 required 标记,最终呈现为前文测试快照中的 OpenAPI 3.1.0 结构。

关于校验失败时的错误形态,仓库测试 tests/test_tutorial/test_body/test_tutorial001.py 给出了丰富的实证:缺少必填字段返回 422 且错误 loc 指向 ["body", "price"]price 传字符串 "twenty" 返回 float_parsing 类型错误;空 body 或无 Content-Type 的请求也会被拒绝;而 application/geo+json 这类合法的 JSON 媒体类型则能正常通过——说明 FastAPI 对 +json 后缀的媒体类型同样宽容处理。

不使用 Pydantic 时的替代方案

如果不想使用 Pydantic 模型,也可以直接使用 Body 参数声明单个标量值。相关内容请见教程 Body - 多个参数:请求体中的单个值(对应英文原文为 docs/en/docs/tutorial/body-multiple-params.md,本指南来源的西班牙语版本见 docs/es/docs/tutorial/body-multiple-params.md),其中详细说明了如何用 Body(...) 把单个值放入请求体并携带约束条件。此外,若继续阅读 docs/zh/docs/tutorial/body-multiple-params.md 之后的系列章节,你还会看到单值 body 与多个 Pydantic 模型共存时 FastAPI 如何自动"嵌入"参数,这正是前文 _should_embed_body_fields() 所对应的用户可见行为。

小结

在本仓库中,请求体的全部官方示例源码集中在 docs_src/body/tutorial001_py310.pytutorial004_py310.py),配套测试位于 tests/test_tutorial/test_body/,底层解析实现则分布在 fastapi/dependencies/utils.pyfastapi/openapi/utils.pyfastapi/params.py。掌握了本文的声明范式,你即可用统一的类型标注风格优雅地组合路径参数、查询参数与请求体,让 FastAPI 自动完成解析、校验、文档生成三大工作,将精力集中在真正的业务逻辑上。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
858
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
923
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.83 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
524
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
393