FastAPI 请求体(Request Body)完全指南:基于 Pydantic 模型的 JSON 数据声明、校验与参数混用实战
2026/9/10 14:41:59 网站建设 项目流程

FastAPI 请求体(Request Body)完全指南:基于 Pydantic 模型的 JSON 数据声明、校验与参数混用实战

【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi

本文围绕 FastAPI 官方教程「Request Body」(韩文翻译版位于 docs/ko/docs/tutorial/body.md)展开,系统讲解如何用 Pydantic 模型声明请求体、FastAPI 据此完成 JSON 读取、类型转换、数据校验、JSON Schema 生成与自动文档,并覆盖请求体与路径参数、查询参数的混用规则。读完本文,你将能够用类型注解的方式编写出带完整校验与自动文档的 POST/PUT 接口,并结合仓库源码与测试用例理解其底层判定逻辑。

请求体与响应体:先厘清两个方向的数据

在 HTTP API 中,数据是分方向流动的:

  • 请求体(Request Body):客户端(例如浏览器或移动端)发送给 API 的数据。
  • 响应体(Response Body):API 返回给客户端的数据。

绝大多数情况下,API 都需要返回响应体;但客户端并不总是需要发送请求体——有时它只请求某个路径,最多带上几个查询参数(query parameters),此时不需要携带任何请求体。

需要向 API 发送数据时,应优先使用以下 HTTP 方法之一:

  • POST(最常见)
  • PUT
  • DELETE
  • PATCH

值得注意的是,把数据放进GET请求的请求体中属于规范未定义的行为(undefined behavior)。虽然 FastAPI 出于极复杂/极端使用场景的考虑仍然支持它,但官方明确不推荐:由于这种用法被劝阻,Swagger UI 之类的交互式文档在使用GET时不会展示请求体文档,中间的代理服务器也可能不支持它。

声明请求体的工具是Pydantic 模型——FastAPI 会调用 Pydantic 的全部能力与收益,这与仓库中 fastapi/dependencies/utils.py 内部对 Pydantic 字段解析、验证的实际实现路径是呼应的。

第一步:导入 Pydantic 的BaseModel

要声明数据模型,首先从pydantic导入BaseModel。完整的示例代码位于 docs_src/body/tutorial001_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()

示例文件使用了 Python 3.10 的类型注解语法(str | None),因此在仓库对应的测试文件中通过needs_py310标记来限定 Python 版本环境(见 tests/test_tutorial/test_body/test_tutorial001.py)。

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

将数据模型声明为继承自BaseModel的类,模型的所有属性都使用标准 Python 类型

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

字段是否必填的规则与声明查询参数时一致:

  • 模型属性带默认值→ 该字段非必填
  • 模型属性没有默认值→ 该字段必填
  • 想让字段可选,最简单的方式是把默认值设为None

例如上面的Item模型,声明的就是一个 JSONobject(等价于 Python 的dict),形如:

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

由于descriptiontax都是可选的(默认值为None),下面这个不包含它们的 JSON 同样是合法的:

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

从 OpenAPI 生成的 schema 中也能印证这一点:测试快照显示Itemrequired列表只包含["name", "price"],而descriptiontax被声明为"anyOf": [{"type": "string"|"number"}, {"type": "null"}]的可空类型(详见 tests/test_tutorial/test_body/test_tutorial001.py 中test_openapi_schema的断言快照)。

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

声明方式与之前声明路径参数、查询参数完全一样——把它放进路径操作函数的参数列表中,并把参数类型标注为你刚创建的模型:

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

只需要这一行类型声明,FastAPI 在请求到达/items/时就会完成以下全部工作:

  1. 读取请求体中的 JSON
  2. 进行必要的类型转换(例如把 JSON 中的字符串数字转成模型声明的float);
  3. 校验数据:若数据不合法,会返回清晰友好的错误,明确指出错误数据是什么、发生在哪里(字段路径);
  4. 把收到的数据注入参数item:因为你在函数中把参数声明为Item类型,编辑器会对该对象的所有属性及其类型提供补全等支持;
  5. 为你的模型生成 JSON Schema 定义——只要对项目有意义,这份 schema 还可以复用在任何其他地方;
  6. 将这些 schema 并入生成的 OpenAPI schema,供自动文档用户界面(UIs)使用。

如何在本地运行与验证

使用与 FastAPI 教程一致的运行方式,即可在本仓库中直接体验:

uvicorn docs_src.body.tutorial001_py310:app --reload

启动后访问http://127.0.0.1:8000/docs查看自动生成的交互式文档,或访问http://127.0.0.1:8000/openapi.json查看完整 OpenAPI schema。由于文档代码文件使用 Python 3.10+ 语法,请确保使用 Python 3.10 及以上版本运行。

自动文档:模型 JSON Schema 如何进入交互式 API 文档

模型的 JSON Schema 会成为生成的 OpenAPI schema 的一部分,并在交互式 API 文档中展示:

同时,凡是需要用到该模型的每个路径操作内部,API 文档也会展示对应的请求体说明与 schema:

也就是说,「定义一次 Pydantic 模型」同时为你带来了运行时校验、数据类型转换与文档三份收益,这正是 FastAPI「基于 Python 类型声明自动生成一切」这一设计的典型体现。

编辑器支持:类型提示贯穿函数体内部

由于你拿到的是一个有真实类型的 Pydantic 模型对象(而不是裸dict),在函数内部所有位置都能获得类型提示与自动补全:

同时,编辑器还能对错误的类型运算给出静态检查提示。官方文档强调这绝非偶然:整个框架正是围绕「让类型信息在编辑器中可用」这一设计目标构建的,在设计阶段、任何实现落地之前就经过了充分测试以保障与各家编辑器兼容,甚至为此推动了 Pydantic 自身的若干改动。上面截图来自 Visual Studio Code,PyCharm 及大多数主流 Python 编辑器都能获得同等支持。

小贴士:如果使用 PyCharm,可以安装 Pydantic PyCharm Plugin,为 Pydantic 模型带来自动补全、类型检查、重构、搜索与检查等增强能力。

在函数体内使用模型:读取属性并加工数据

模型对象在函数内部可以直接按属性访问,也可以借助 Pydantic 的方法把模型转回字典做进一步处理。参考 docs_src/body/tutorial002_py310.py:

@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

这里使用了 Pydantic v2 的model_dump()方法把模型实例序列化为字典,随后在tax存在的前提下追加计算price_with_tax字段并返回。相应的行为在 tests/test_tutorial/test_body/test_tutorial002.py 中有完整断言:请求体中price"50.5"(字符串)或50.5(数字)都能被正确转换为浮点数并计算出price_with_tax: 50.8;不传tax时则不会出现该字段。

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

路径参数与请求体可以在同一个路径操作函数里同时声明:

@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。FastAPI 会自动识别:

  • 路径参数匹配的函数参数 → 从路径中取值;
  • 声明为Pydantic 模型类型的函数参数 → 从请求体中取值。

测试 tests/test_tutorial/test_body/test_tutorial003.py 验证了PUT /items/123item_id被解析为整数、请求体被解析为Item模型的完整链路,其 OpenAPI 快照也证明:item_id出现在parametersin: "path"type: "integer"),而Item出现在requestBody中并被标记为required: true

请求体 + 路径参数 + 查询参数:三管齐下

请求体路径参数查询参数三者可以在同一函数中同时声明,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

函数参数的最终判定规则如下:

  1. 若参数同时出现在路径中 → 作为路径参数处理;
  2. 若参数是单一类型(如intfloatstrbool等)→ 被解释为查询参数
  3. 若参数声明为Pydantic 模型类型→ 被解释为请求

对应的测试 tests/test_tutorial/test_body/test_tutorial004.py 会同时携带路径/items/123、查询串q=somequery和 JSON 请求体发起PUT请求,并断言返回结果中三部分数据全部就位;其 OpenAPI 快照中qrequired: false出现在in: "query"参数列表里。

关于可选性判定还有一个易被忽略的细节(官方 note 特别强调):FastAPI 判断q是否必填依据的是默认值= None,而不是str | None这个类型注解本身。也就是说,str | None并不会告诉 FastAPI「该值可选」,真正起作用的是默认值= None。但保留类型注解依然有价值——它能让编辑器提供更好的支持并及早发现潜在错误。

底层视角:FastAPI 如何识别请求体并完成校验

从源码看,FastAPI 依赖在 fastapi/dependencies/utils.py 中实现的request_body_to_args()函数来执行请求体解析与字段校验的核心逻辑:当路径操作函数中存在被识别为 body 的参数时,请求体会被读取、按模型字段逐一提取并交给 Pydantic 校验,然后把结果组装回参数中(request_body_to_args()在文件约第 951 行起实现)。这与本文前面总结的「读取 JSON → 类型转换 → 校验 → 注入参数」行为完全对应。

仓库中针对本教程的测试用例(tests/test_tutorial/test_body/test_tutorial001.py)从多个角度印证了请求体校验的细节,非常值得阅读:

  • price传字符串"50.5"会被转换并响应 200(类型转换);
  • 缺少必填字段price时返回422,错误定位为["body", "price"],错误类型missing
  • price"twenty"时返回 422,错误类型float_parsing,并给出「无法将字符串解析为数字」的提示;
  • 请求体为损坏 JSON 时返回 422,错误类型json_invalid(JSON decode error);
  • 携带不匹配的Content-Type(如text/plain)或发送表单格式(form data)给期望 JSON 的接口时,返回 422 并提示需要合法的字典/对象输入;
  • GET /openapi.json则能拿到包含ItemValidationErrorHTTPValidationError三套组件的完整 OpenAPI schema 快照。

这说明 422 校验错误的响应结构(detail数组中的typelocmsginputctx等字段)是有稳定格式可依赖的,客户端可以据此实现友好报错提示。

不依赖 Pydantic 时怎么办

如果你不想使用 Pydantic 模型,FastAPI 也提供了Body参数来直接声明单值请求体。这一路径的用法与多个参数共存的处理方式,请参见 Body - Multiple Parameters 一文中的 "Singular values in body"(正文中的单值)一节,那里会讲解当请求体里只有单个值时如何绕过模型直接声明。在动手组合更复杂的请求体场景前,建议先按本教程的顺序完成模型声明、参数混用与错误处理等基础练习。

【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询