首页
/ FastAPI 请求体(Request Body)完全指南:用 Pydantic 模型声明与校验 POST/PUT 数据

FastAPI 请求体(Request Body)完全指南:用 Pydantic 模型声明与校验 POST/PUT 数据

2026-09-07 14:43:21作者:何将鹤

本指南以 FastAPI 官方教程「请求体(Request Body)」为核心,系统讲解如何用 Pydantic 模型接收客户端(如浏览器、移动端)提交的 JSON 数据,涵盖模型定义、必填/可选字段声明、与路径参数、查询参数的混用规则,并结合仓库内 docs_src/body/ 下的示例源码与 tests/test_tutorial/test_body/ 中的测试用例,向你展示 FastAPI 如何把「类型声明」变成「JSON 解析 + 数据校验 + OpenAPI 文档」一整条自动化链路。读完本文,你将能独立写出健壮的 POST/PUT 接口,并精确理解每个参数最终落在请求的哪个位置。

什么是请求体,何时需要发送

当客户端(比如浏览器或移动 App)需要向你的 API 发送数据时,数据通常以 请求体(request body) 的形式发送。相应地,响应体(response body) 是你的 API 返回给客户端的数据。

需要区分的是:API 几乎总要返回响应体,但客户端并不总是需要发送请求体——有时客户端只是请求某个路径,可能带上几个查询参数,但不会携带 body。因此,FastAPI 教程对请求体的定位是:在“确有必要”时才用它来承载结构化数据。

关于请求方法,官方文档给出了明确提醒:

  • 要发送数据,应使用 POST(最常见)、PUTDELETEPATCH 之一;
  • GET 请求中发送 body,在规范(HTTP 规范)中属于未定义行为。FastAPI 出于对非常复杂/极端场景的兼容性仍然支持它,但这是被强烈不鼓励的做法——Swagger UI 交互式文档不会为 GET 展示 body 文档,且中间代理(proxy)也很可能不支持这种用法。

声明请求体的方式十分简单:使用 Pydantic 模型——借助 Pydantic 的全部能力与优势来完成类型转换、校验和序列化。

第一步:导入 Pydantic 的 BaseModel

在开始前,先引入必要的依赖。示例代码位于 docs_src/body/tutorial001_py310.py,首先从 pydantic 导入 BaseModel

from fastapi import FastAPI
from pydantic import BaseModel

这里的 fastapi.FastAPI 用于创建应用实例,而 BaseModel 是你定义数据模型的基类。

第二步:定义数据模型(Data Model)

把数据模型声明为继承自 BaseModel 的类,并使用标准的 Python 类型标注所有属性:

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

必填与可选字段的约定

与声明查询参数时的规则一致:

  • 模型属性有默认值 → 该字段非必填
  • 模型属性没有默认值 → 该字段必填
  • 使用 None 作为默认值即可让字段可选

在上面的 Item 模型中,name: strprice: float 没有默认值,因此必填;descriptiontax 的默认值为 None,因此是可选的。这个模型对应一个 JSON「对象」(即 Python 的 dict),例如:

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

因为 descriptiontax 可选,下面的 JSON 同样是合法的请求体:

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

可选项的文本内容,但别选错可选语义

注意区分两种“可选”:

  • str | None(Python 3.10+ 联合类型语法)只表达“类型上允许为 None”,它本身并不决定字段是否必填
  • 真正让字段“非必填”的是 = None 这个默认值。

这一点在下文混用查询参数时会再次印证。

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

路径操作(path operation)函数里,把它当作参数声明即可——就像之前声明路径参数和查询参数那样,并把类型标注为你创建的模型 Item

app = FastAPI()


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

只靠这一处 Python 类型声明,FastAPI 就会自动完成:

  1. 将请求体作为 JSON 读取
  2. 转换对应的类型(如有需要,比如把字符串 "50.5" 转成 float);
  3. 校验数据——若数据非法,会返回清晰明确的错误,精确指出错误位置与错误内容(HTTP 422 与 ValidationError 结构);
  4. 把接收到的数据放入参数 item——由于你在函数中把 item 的类型声明为 Item,编辑器会对该参数的全部属性及其类型提供自动补全等支持;
  5. 为模型生成 JSON Schema 定义,这些 Schema 可被项目在其他地方复用;
  6. 这些 Schema 会成为生成的 OpenAPI Schema 的一部分,并被自动文档 UI 使用。

完整可运行的示例见 docs_src/body/tutorial001_py310.py

源码级佐证:请求体在 OpenAPI 中如何呈现

仓库中的测试 tests/test_tutorial/test_body/test_tutorial001.py 对生成的 /openapi.json 做了快照断言(test_openapi_schema)。从中可以看到,POST /items/ 的 OpenAPI 定义里出现了:

"requestBody": {
    "content": {
        "application/json": {
            "schema": {"$ref": "#/components/schemas/Item"}
        }
    },
    "required": true
}

同时 components.schemas.Item 被生成为 type: object,其中 required 列表恰好是 ["name", "price"](两个无默认值的字段),而 descriptiontax 被描述为 anyOf: [{"type": "string"}, {"type": "null"}] / anyOf: [{"type": "number"}, {"type": "null"}]。这就是“默认值决定必填性、类型标注决定 Schema”最直观的代码证据。

校验失败的真实形态(来自测试断言)

同样是上述测试文件,覆盖了各种异常请求并断言返回 HTTP 422:

请求场景 结果
缺少必填字段(如只传 {"name": "Foo"} 422,错误 type: "missing"loc: ["body", "price"]
传了无法解析为数字的字符串(price: "twenty" 422,错误 type: "float_parsing"
请求体为空对象 {} 422,同时报告 nameprice 缺失
请求体为 null 422,错误定位到 loc: ["body"]
JSON 语法损坏 422,错误 type: "json_invalid",并附 ctx.error
提交表单而非 JSON(application/x-www-form-urlencoded 422
Content-Type 不是 JSON 类型 422

反过来,只要 Content-Type: application/json(或 application/geo+json 等 JSON 变体)且数据合法,返回就是 200。另外测试还演示了类型强制转换price 传字符串 "50.5" 时,响应中会变成浮点数 50.5——这正是“按需转换类型”的直接证据。

在函数中使用模型对象

进入函数内部后,可以直接访问模型对象的各个属性。教程的进阶版示例 docs_src/body/tutorial002_py310.py 展示了更真实的业务用法:先把模型转为 dict,再依据可选字段动态加工数据。

@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.model_dump()(Pydantic v2 中替代旧版 item.dict() 的方法)把模型实例序列化为普通 dict,随后检查可选字段 item.tax 是否真的传入了值,只有非 None 时才计算含税价格并追加键 price_with_tax

对应的测试 tests/test_tutorial/test_body/test_tutorial002.py 也很有意思:

  • tax: 0.3 时,响应为 {"name": "Foo", "price": 50.5, "description": "Some Foo", "tax": 0.3, "price_with_tax": 50.8}——证明 price_with_tax 由 50.5 + 0.3 计算而来;
  • 不传 tax 时,响应中不含 price_with_tax 键,taxNone——证明 model_dump() 保留了可选键为 None,而条件分支正确地跳过了计算;
  • 测试用参数化同时验证了 price"50.5" 字符串与 50.5 浮点数时都能得到相同的 50.5 数值结果,再次佐证类型转换能力。

请求体与路径参数同时使用

你完全可以同时声明路径参数与请求体。FastAPI 会智能识别:函数参数中与路径参数同名/匹配的,从路径中取值;被声明为 Pydantic 模型类型的参数,则从请求体中读取

来看示例 docs_src/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 出现在路径 "/items/{item_id}" 中,因此被当作路径参数解析为 intitem 的类型是 Pydantic 模型,于是被当作请求体解析。返回值把路径中的 item_id 与模型字段合并成一个响应对象。

测试 tests/test_tutorial/test_body/test_tutorial003.py 中用 client.put("/items/123", json={...}) 验证:路径中的 "123" 被转换为整数 123,响应为 {"item_id": 123, ...}。同时该测试生成的 OpenAPI 快照中,路径参数 item_id 位于 parametersin: "path"required: truetype: "integer"),而请求体独立出现在 requestBody——这说明二者在文档和解析上完全分离、互不干扰。

请求体 + 路径参数 + 查询参数三者混用

更进一步,你还可以在同一个接口里同时声明请求体、路径参数和查询参数,FastAPI 会为每个参数自动从正确位置取值。示例见 docs_src/body/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

调用 /items/123?q=somequery 并携带 JSON 请求体时,item_id 来自路径、q 来自查询串、item 来自请求体。

FastAPI 的参数识别规则

函数参数会被按如下规则归类(官方文档明确给出):

  • 若参数同时声明在路径中 → 作为路径参数
  • 若参数是单一类型(如 intfloatstrbool 等)→ 作为查询参数
  • 若参数类型被声明为 Pydantic 模型 → 作为请求

关于 q: str | None = None 的必填性说明

文档特别提示:FastAPI 判断 q 非必填,依据的是默认值 = None而不是 str | None 这个类型注解本身。不加默认值的 q: str 就会变成必填查询参数。当然,写出 str | None 这样的类型注解依然有意义——它能让编辑器给出更好的补全与错误提示,这属于“类型正确性”的范畴。

对应的测试 tests/test_tutorial/test_body/test_tutorial004.py 做了两点关键验证:

  1. 行为上:client.put("/items/123", json={...}, params={"q": "somequery"}) 时响应包含 "q": "somequery";不带 q 时响应不含该键。
  2. 文档上:OpenAPI 快照中 q 出现在 parameters 里,且为 "required": falsein: "query" 参数;模型 Item 仍然只在 requestBody 中被引用。这正好与“默认值 None → 非必填”及“单值类型 → 查询参数”两条规则互相印证。

自动生成的交互式文档

由于模型会被纳入 OpenAPI Schema,自动生成的交互式 API 文档会直接展示你的数据模型,例如:

你可以访问应用根路径的 /docs(Swagger UI)直接试发请求体并观察校验响应。

编辑器的类型提示与自动补全支持

使用 Pydantic 模型而非裸 dict 的另一个巨大好处是编辑器支持

这种体验并非巧合:FastAPI 整个框架正是围绕“类型声明驱动”这一设计目标构建的,并且在实现之前就经过了设计阶段的严格测试,以确保能与各家编辑器协同工作,Pydantic 本身也为此做过相应改动。上面的截图来自 Visual Studio Code,但在 PyCharm 及大多数主流 Python 编辑器中也有一致的体验(PyCharm 下的效果见 docs/en/docs/img/tutorial/body/image05.png)。若使用 PyCharm,还可以安装 Pydantic PyCharm Plugin 来获得对 Pydantic 模型更完善的自动补全、类型检查、重构、搜索与代码检查能力。

不使用 Pydantic 的替代方案

如果你不想使用 Pydantic 模型,也可以直接用 Body 参数来接收请求体中的单个值。相关内容属于「请求体 – 多参数」教程的范畴,详见官方文档 docs/en/docs/tutorial/body-multiple-params.md#singular-values-in-body 中「body 中的单值」一节(法语文档入口见 docs/fr/docs/tutorial/body.md 结尾的指引)。

小结

从一次简单的 POST /items/ 到「路径参数 + 查询参数 + 请求体」三合一接口,FastAPI 请求体的核心心智模型只有一句话:类型声明即一切。写对类型、写对默认值,FastAPI 就替你完成了 JSON 读取、类型转换、数据校验、422 错误响应与 OpenAPI 文档生成的全部工作;而 Pydantic 模型带来的编辑器补全与静态检查,则让这类代码从「能跑」走向「可靠、可维护」。想进一步实践,可以运行仓库内 docs_src/body/ 下的四个示例,并用 tests/test_tutorial/test_body/ 目录中的测试用例对照学习各种边界情况(缺字段、坏 JSON、错误 Content-Type、类型强转等)。

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

项目优选

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