FastAPI 请求体字段校验与元数据:使用 Pydantic Field 为 Body 模型添加验证规则
本篇技术指南围绕 FastAPI 教程「Body - Fields」章节展开,讲解如何在 Pydantic 模型中借助 Field 声明额外的校验规则与元数据,使请求体字段获得与 Query、Path、Body 参数一致的声明能力。读完本文,你将掌握 Field 的导入方式、模型属性声明语法、常用校验与元数据参数、JSON Schema / OpenAPI 的生成机制,以及底层 FieldInfo 与 Param 的继承关系。
为什么需要 Field:从函数参数到模型字段
在 FastAPI 中,你可以用 Query、Path、Body 为 路径操作函数 的参数声明额外校验和元数据,例如长度限制、数值范围、标题与描述。但请求体往往不是一个简单参数,而是一个 Pydantic 模型——模型的每个字段同样需要校验和元数据。这时就要用到 Pydantic 提供的 Field。
原文档 docs/pt/docs/tutorial/body-fields.md(以及英文原版 docs/en/docs/tutorial/body-fields.md)明确指出:就像可以用 Query、Path 和 Body 在路径操作函数参数中声明附加校验与元数据一样,你可以用 Pydantic 的 Field 在 Pydantic 模型内部声明校验和元数据。
导入 Field:来自 pydantic,而非 fastapi
Field 的导入方式与 Query、Path、Body 有一个关键区别,原文档特意用警告框强调:
from pydantic import BaseModel, Field
警告:
Field是直接从pydantic导入的,而不是像Query、Path、Body等那样从fastapi导入。
这一点在仓库源码中得到印证:FastAPI 自己的 Query、Path 等是定义在 fastapi/param_functions.py 中的函数,而 Field 完全属于 Pydantic 的领域,FastAPI 只是依赖 Pydantic 的 FieldInfo 体系来完成参数解析。
声明模型属性:完整可运行示例
原文档引用的示例代码位于 docs_src/body_fields/tutorial001_an_py310.py(使用 Annotated 风格)和 docs_src/body_fields/tutorial001_py310.py(使用默认值风格)。下面是基于 Annotated 版本的完整代码:
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results
这里 Field 的应用要点:
description字段:类型str | None,默认值None,通过Field声明了title(在 schema 中作为字段标题)和max_length=300(字符串最大长度)。price字段:通过Field(gt=0, ...)声明数值必须大于 0,并附加description说明。tax字段:未使用Field,仅作为普通可空字段存在,用于对比展示。- 路径操作函数中
item: Annotated[Item, Body(embed=True)]将请求体以{"item": {...}}的形式嵌入,这正是 fastapi/params.py 中Body类embed参数的用途。
原文档还特别提示:每个带有类型、默认值和 Field 的模型属性,其结构与路径操作函数的参数完全一致——只不过把 Path、Query、Body 换成了 Field。也就是说,你在参数上学会的一切声明技巧,都可以原样搬到模型字段上。
Field 与 Query / Path / Body 的统一原理
为什么 Field 能和 Query、Path、Body 提供几乎一致的参数?原文档的「技术细节」框给出了答案,而仓库源码进一步印证了这条继承链:
Param基类:Query、Path、Header、Cookie创建的对象都是公共基类Param的子类实例,而Param本身继承自 Pydantic 的FieldInfo。见 fastapi/params.py 中class Param(FieldInfo)及Path、Query、Header、Cookie均以class Xxx(Param)定义。Field返回FieldInfo:Pydantic 的Field同样返回一个FieldInfo实例,与Param处在同一类型体系中。Body直接继承FieldInfo:与Param不同,Body是直接继承FieldInfo的类(fastapi/params.py),而Form、File又继承自Body,后续教程中你会看到这些子类。- 函数包装:当你从
fastapi导入Query、Path等时,它们实际上是返回特殊类的函数(定义于 fastapi/param_functions.py),内部将参数转发给 fastapi/params.py 中对应的类。
正因为 Field 和 Query/Path/Body 最终都收敛到 Pydantic 的 FieldInfo,FastAPI 才能用同一套机制把它们生成的 schema 信息整合进 OpenAPI。
Field 常用参数速查
从 fastapi/param_functions.py 中 Query、Path 等函数的签名可以看出,Field 支持的同名参数非常丰富(Field 拥有这些参数的 Pydantic 实现),按用途可分为以下几类:
| 类别 | 参数 | 说明 |
|---|---|---|
| 基础 | default |
字段默认值;不传则字段必填 |
| 基础 | default_factory |
生成默认值的可调用对象 |
| 元数据 | title |
人类可读的标题,会进入 JSON Schema |
| 元数据 | description |
人类可读的描述,会进入 JSON Schema |
| 字符串校验 | min_length / max_length |
字符串最小 / 最大长度 |
| 字符串校验 | pattern |
字符串正则表达式(regex 已废弃,改用 pattern) |
| 数值校验 | gt / ge / lt / le |
大于 / 大于等于 / 小于 / 小于等于,仅适用于数值 |
| 数值校验 | multiple_of |
数值必须是该值的倍数 |
| 数值校验 | allow_inf_nan |
是否允许 inf、-inf、nan |
| 数值校验 | max_digits / decimal_places |
小数值的最大位数 / 最大小数位 |
| 别名 | alias / validation_alias / serialization_alias |
字段别名及校验、序列化阶段的别名 |
| 示例 | examples / openapi_examples |
字段示例(example 已废弃) |
| 其他 | deprecated |
标记字段为废弃 |
| 其他 | include_in_schema |
是否包含进生成的 OpenAPI |
| 扩展 | json_schema_extra / 额外关键字 |
附加 JSON Schema 数据 |
需要注意两点:
regex参数在 FastAPI 0.100.0 及 Pydantic v2 中已废弃,应使用pattern;example在 OpenAPI 3.1.0(使用 JSON Schema 2020-12)中已废弃,应使用examples。这些废弃提示均以FastAPIDeprecationWarning形式出现在 fastapi/params.py 等处的源码中。Path参数不允许默认值,源码中通过assert default is ...强制(fastapi/params.py),这是路径参数特有的约束,模型字段不受此限。
添加额外信息:从 Field 到 JSON Schema 与 OpenAPI
你可以在 Field(以及 Query、Body 等)中声明额外信息,这些信息会进入生成的 JSON Schema。原文档指出,关于声明示例(examples)等高级用法,将在后续「Schema 额外示例」章节详细学习(对应文档 docs/pt/docs/tutorial/schema-extra-example.md)。
额外信息如何影响最终产物?看仓库测试 tests/test_tutorial/test_body_fields/test_tutorial001.py 中对 /openapi.json 的断言即可一目了然:
description字段的title="The description of the item"出现在Itemschema 的属性标题中,maxLength: 300被合并进anyOf中的字符串分支;price的gt=0转换为exclusiveMinimum: 0.0,description原样保留;tax没有任何额外约束,schema 中只有普通的可空number分支;- 由于使用了
Body(embed=True),还生成了一个Body_update_item_items__item_id__put包装 schema,其required列表包含item。
警告(原文档重点):传给
Field的额外键也会出现在应用最终的 OpenAPI schema 中。由于这些键未必是 OpenAPI 规范的一部分,一些 OpenAPI 工具(例如 OpenAPI 校验器)可能无法正确处理你生成的 schema。因此,自定义额外键应谨慎使用,并明确其可能带来的兼容性问题。
校验行为验证:测试用例如何证明 Field 生效
仓库测试 tests/test_tutorial/test_body_fields/test_tutorial001.py 从四个维度验证了 Field 的行为,可作为你自行验证的参考:
- 合法请求(
test_items_5):PUT /items/5,body 为{"item": {"name": "Foo", "price": 3.0}},返回 200,且缺失的description、tax被补为None。 - 类型隐式转换(
test_items_6):tax传入字符串"5.4"被转换为浮点数5.4,展示了 Pydantic 的类型转换能力。 - 校验失败(
test_invalid_price):price: -3.0触发gt=0约束,返回 422,错误结构为:{ "type": "greater_than", "loc": ["body", "item", "price"], "msg": "Input should be greater than 0", "input": -3.0, "ctx": {"gt": 0.0} }loc路径精确到body → item → price,可直接用于前端错误定位。 - OpenAPI schema 快照(
test_openapi_schema):完整断言上述Itemschema 与包装 schema 的生成结果。
此外,该测试通过 fixture 同时运行 tutorial001_py310 与 tutorial001_an_py310 两个版本的示例(tests/test_tutorial/test_body_fields/test_tutorial001.py),说明「默认值风格」与「Annotated 风格」两种写法行为完全等价。
回顾总结
- 使用 Pydantic 的
Field可以为模型属性声明额外校验和元数据,功能与Query、Path、Body对齐。 Field必须从pydantic导入;Query、Path、Body从fastapi导入。- 底层上,
Field返回FieldInfo实例,Query/Path等返回Param(FieldInfo子类)实例,Body直接返回FieldInfo子类实例,三者统一于 Pydantic 的字段信息体系。 - 校验参数(
gt、max_length、pattern等)会转化为 JSON Schema 约束并最终体现在 OpenAPI 文档与 422 校验错误中。 - 额外关键字可用于传递附加 JSON Schema 元数据,但需注意 OpenAPI 工具兼容性。
- 相关资源:示例代码、参数实现、参数函数定义、测试用例。
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 StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00