首页
/ FastAPI 请求体(Request Body)实战指南:基于 Pydantic 模型完成数据接收、类型转换与自动校验

FastAPI 请求体(Request Body)实战指南:基于 Pydantic 模型完成数据接收、类型转换与自动校验

2026-09-06 18:14:08作者:秋泉律Samson

本篇技术指南聚焦 FastAPI 中请求体(Request Body)的完整使用方式:如何在路径操作函数中以 Pydantic 模型声明请求体、如何与路径参数和查询参数混用、FastAPI 在背后自动完成的 JSON 解析与校验逻辑,以及如何借助源码与测试验证这些行为。读完本文,你将能独立写出带类型安全、自动文档与编辑支持的生产级请求体接口。

请求体(Request Body)是什么

当客户端(例如浏览器)需要向你的 API 发送数据时,这些数据以 请求体(request body) 的形式提交;相应地,API 返回给客户端的数据被称为响应体(response body)

需要明确两点基础认知:

  • API 几乎总是需要发送响应体
  • 客户端则不一定每次都要发送请求体——有时客户端只请求某个路径,最多附带几个查询参数,而不携带请求体。

在 FastAPI 中,声明请求体使用 Pydantic 模型,你可以直接获得 Pydantic 的全部能力与收益(类型转换、数据校验、JSON Schema 生成等)。

注意(HTTP 方法选择)

要发送数据,应使用 POST(最常见)、PUTDELETEPATCH 之一。

在规范中,GET 请求携带请求体属于“行为未定义”,不过 FastAPI 出于对极复杂/极端场景的兼容仍然支持它。但由于这种做法不被推荐,Swagger UI 交互式文档在使用 GET 时不会展示请求体的说明,且中间的代理服务器可能也不支持。

第一步:声明请求体数据模型

导入 Pydantic 的 BaseModel

首先要从 pydantic 导入 BaseModel

from fastapi import FastAPI
from pydantic import BaseModel

创建数据模型

然后声明一个继承自 BaseModel 的类作为数据模型,属性使用标准 Python 类型:

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

这段代码的完整示例位于 docs_src/body/tutorial001_py310.py。需要特别说明模型属性语义:

  • 与声明查询参数时的规则一致:当模型属性带有默认值时,它不是必填的;否则就是必填的
  • 使用 None(并配合 str | Nonefloat | None 这类可选类型注解)可以让字段“仅仅是可选”。

因此上面的模型描述的是一个 JSON "object"(等价于 Python dict),例如:

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

由于 descriptiontax 是可选字段(默认值为 None),下面这个 JSON "object" 同样是合法的:

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

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

要把它加入 路径操作(path operation),只需像声明路径参数与查询参数那样声明,并把参数类型标注为刚创建的模型 Item

app = FastAPI()


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

完整代码见 docs_src/body/tutorial001_py310.py。这里没有写任何手动解析 JSON 的代码——仅凭这一个类型声明,任务就完成了。

一次类型声明,FastAPI 自动完成的全部工作

仅仅依靠上面的 Python 类型声明,FastAPI 就会自动:

  1. 读取请求体并将其解析为 JSON
  2. 按需转换对应的类型(例如把 JSON 中的数字字符串 "50.5" 转换成 float);
  3. 校验数据——如果数据无效,返回清晰友好的错误,精确指出错误的数据位置与内容;
  4. 把接收到的数据放入参数 item——由于在函数中声明类型为 Item,所有属性及其类型都会获得完整的编辑器支持(补全、提示等);
  5. 为模型生成 JSON Schema 定义,这些 Schema 可以复用于项目中任何合理的地方;
  6. 这些 Schema 会并入生成的 OpenAPI Schema,并被自动文档用户界面(UI)所使用。

源码与测试如何印证上述行为

在 FastAPI 源码中,请求体最终由 fastapi/params.py 中的 Body 字段信息类承载,其默认 media_typeapplication/json,也就是说请求体会按 JSON 媒体类型解析。而“读取并校验 JSON、返回 422”的行为,在仓库测试 tests/test_tutorial/test_body/test_tutorial001.py 中有着极为细致的覆盖,可直接作为验收依据:

  • 类型转换{"name": "Foo", "price": "50.5"} 中字符串形式的 "50.5" 会被转换为 float 类型的 50.5,返回 200 与完整模型 JSON(见 test_tutorial001.py);
  • 缺少必填字段{"name": "Foo"} 因缺少 price 返回 422,错误信息中 loc 精确定位到 ["body", "price"]msgField required(见 test_tutorial001.py);
  • 非法类型值{"name": "Foo", "price": "twenty"} 返回 422,错误类型为 float_parsing(见 test_tutorial001.py);
  • 残缺 JSON 正文{some broken json} 这类无法解码的内容会返回 422,错误类型为 json_invalidloc 指向 ["body", 1](见 test_tutorial001.py);
  • 错误媒体类型:以 text/plainapplication/geo+json-seq 等非 JSON 类型提交会被拒绝,错误类型为 model_attributes_type(见 test_tutorial001.py)。

这些测试同时表明:校验错误遵循 OpenAPI 的 ValidationError / HTTPValidationError 结构,loc 中的 "body" 前缀正好证明数据来源是请求体。

自动文档:模型 Schema 出现在交互式 API 文档中

你的模型 JSON Schema 会进入自动生成的 OpenAPI Schema,并展示在交互式 API 文档里。下图为文档首页展示的由模型生成的 Schema(含 ItemValidationErrorHTTPValidationError 错误模型):

FastAPI 交互式 API 文档中由 Item 模型生成的请求体 Schema 与校验错误模型定义

而在每个使用到该模型的 路径操作 内部,同样会展示请求体示例、application/json 媒体类型与 200/422 响应说明,并支持 "Try it out" 在线调试:

FastAPI 交互式文档中 POST /items/ 路径操作的请求体模型、示例 JSON 与状态码说明

当你需要验证自己写好的接口时,可启动应用后访问 /docs(Swagger UI)或 /redoc 查看上述界面。

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

在编辑器里,函数内部你将随处获得类型提示与自动补全——如果你收到的是 dict 而不是 Pydantic 模型,这一切都不会发生:

编辑器中对 Item 模型属性 item.name 的字符串方法自动补全提示

同时,编辑器还能针对错误的类型操作给出错误检查。FastAPI 官方文档指出,这并非偶然——整个框架就是围绕这一设计构建的,并且在设计阶段、任何实现之前就进行了大量测试以确保能与所有主流编辑器协同工作,甚至为此对 Pydantic 本身做过修改。上述截图取自 Visual Studio Code;在 PyCharm 以及大多数其他 Python 编辑器中也能获得同样的支持。若使用 PyCharm,还可以搭配 Pydantic 的 PyCharm 插件,获得自动补全、类型检查、重构、搜索与检查等增强能力。

在函数内部使用模型对象

进入函数后,你可以直接访问模型对象的各个属性。下面的例子把模型转换为 dict(通过 Pydantic v2 的 model_dump()),并根据可选的 tax 字段动态计算含税价格:

@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

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

可以同时声明路径参数与请求体。FastAPI 会自动识别:与路径参数匹配的函数参数从路径中取值,而声明为 Pydantic 模型的函数参数从请求体取值

@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 会逐一识别并把数据取到正确的位置:

@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。对应的测试 tests/test_tutorial/test_body/test_tutorial004.py 验证了同时携带路径值 123、请求体四个字段与查询参数 q=somequery 时,返回结果完整包含 item_id、模型字段与 q

函数参数如何被判定来源

当多个类型的参数混合出现时,FastAPI 按以下规则判定每个参数的来源:

  • 如果参数同时出现在路径中,它会被用作路径参数
  • 如果参数是单一类型(如 intfloatstrbool 等),它会被解释为查询参数
  • 如果参数的类型是一个 Pydantic 模型,它会被解释为请求

关于可选参数 q 的判定细节

FastAPI 判断 q 非必填的依据不是类型注解 str | None,而是因为它有默认值 = None。不过加上类型注解依然有价值——它让你的编辑器提供更好的支持并及早发现错误。这一点已在 docs/en/docs/tutorial/body.md 的官方说明中强调。

从生成的 OpenAPI 模式看参数判定结果

在测试 tests/test_tutorial/test_body/test_tutorial004.py 的 OpenAPI 快照中可以看到:/items/{item_id}PUT 操作中,item_id 出现在 parametersin: pathrequired: trueq 出现在 parametersin: queryrequired: false,而 Item 模型则作为 requestBody.content["application/json"].schemarequired: true。请求体的必填性、三种数据来源的区分,都被精确地写入了 OpenAPI 定义,这也是自动文档能正确渲染的前提。

不想用 Pydantic?使用 Body 参数

如果不希望使用 Pydantic 模型,也可以直接使用 Body 参数把单一类型的值放入请求体。详见文档 Body - Multiple Parameters: Singular values in body,那里介绍了如何在 body 中携带单个标量值及其与模型共存时的写法。

小结与后续学习路径

请求体的核心心智模型是:“声明 Pydantic 模型类 → 作为路径操作函数参数的类型注解”,其余(JSON 读取、类型转换、校验、错误响应、Schema 生成、编辑器支持)都由 FastAPI 自动完成。本文对应的原始教程位于 docs/en/docs/tutorial/body.md,可运行示例集中在 docs_src/body/,行为验证测试位于 tests/test_tutorial/test_body/

在掌握单一模型请求体后,建议继续深入学习以下进阶主题(均为 FastAPI 官方教程同系列内容):

掌握这些之后,无论是 REST 资源写入、批量提交还是复杂嵌套的业务数据结构,你都能用最少的样板代码写出类型安全且自文档化的接口。

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