首页 / FastAPI 入门教程 / 请求体字段校验

FastAPI 入门教程

请求体字段校验

本教程共 50 篇 · 第 11 篇 · 更新于 2026-08-12 · 约 7 分钟阅读

FastAPIFastAPI 入门教程字段校验Fieldgtexamples

本节目标:学会用 Pydantic 的 Field 给模型字段加约束条件(如价格必须大于 0、名称长度限制),理解必填与可选的区别,并知道如何在校验失败时给出更友好的提示。

上一章我们只是声明了字段的类型。类型本身已经是一种校验:字符串必须是字符串,数字必须是数字。但真实业务往往还有更细的规则。

比如商品的价格不能是负数,用户名不能太长。这些额外规则,就要靠 Field 来写。

11-1 为什么需要字段级校验

只写类型,只能保证「这是个数字」。可数字可以是 -1000999999。很多时候这都不合理。

字段级校验让我们把业务规则直接写进模型定义里。规则跟着字段走,代码集中、好维护,而且校验逻辑不用你手写,Pydantic 会自动执行。

它的写法和查询参数里的 Query、路径参数里的 Path 很像,只是 Field 用在模型内部。

11-2 导入 Field

Field 不是从 fastapi 导入的,而是直接从 pydantic 导入。这一点要记牢,很多人会搞混。

from fastapi import FastAPI
from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(..., title="商品名称", min_length=1, max_length=50)
    description: str | None = Field(
        None, title="商品描述", max_length=300
    )
    price: float = Field(..., gt=0, description="价格必须大于 0")
    tax: float | None = Field(None, ge=0)

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item.model_dump()}

注意 Field 来自 pydantic,而 QueryPathBody 来自 fastapi。它们底层其实是亲戚,但导入位置不同。

11-3 常用约束参数

Field 支持一堆约束参数,下面是最常用的几个:

  • gt:大于(greater than)。gt=0 表示必须比 0 大。
  • ge:大于等于(greater or equal)。ge=0 表示可以为 0。
  • lt:小于(less than)。
  • le:小于等于(less or equal)。
  • min_length:字符串最小长度。
  • max_length:字符串最大长度。

这些参数只对合适类型生效。比如 gt 用在数字上,min_length 用在字符串上。

回到上面的例子:price 用了 gt=0,所以价格不能是负数;name 用了 min_length=1max_length=50,名字既不能空也不能超长。

Tip

gtgeltle 来自英文缩写,记住「g 是大于、l 是小于、e 是等于」就不容易混。比如 ge=18 就是「大于等于 18」。

11-4 必填与可选怎么写

Field 里,第一个位置参数控制「默认值」。它决定了字段是否必填:

  • Field(...) 里的 ...(三个点,叫 Ellipsis)表示没有默认值,也就是必填。
  • Field(None) 表示默认值是 None,也就是可选。
  • Field(0) 表示默认值是 0,同样是可选,且缺省时取 0

所以 name: str = Field(...) 是必填的,而 tax: float | None = Field(None) 是可选的。

类型注解里的 str | None 只是告诉编辑器「这里可能是字符串,也可能是 None」,真正决定必填与否的是那个默认值。两者配合写,既清晰又安全。

11-5 给字段加元数据

除了约束,Field 还能接收纯描述性的元数据,它们会出现在自动生成的文档里:

  • title:字段标题。
  • description:字段说明。
  • examples:示例值,展示在文档中,方便调用方理解格式。
from fastapi import FastAPI
from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(..., title="商品名称", examples=["机械键盘"])
    price: float = Field(..., gt=0, description="价格必须大于 0", examples=[299.0])
    tax: float | None = Field(None, ge=0, examples=[19.9])

app = FastAPI()

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

打开 /docs,点开 Item 模型,就能看到这些 titledescriptionexamples。它们不影响运行,但能大幅提升接口的可读性。

Note

examples 是 Pydantic 和 FastAPI 都支持的参数,会被写进 OpenAPI 文档。它和「默认值」不同:示例值只是展示用,不会在请求缺失时自动填充。

11-6 校验失败时报错长什么样

当客户端发来的数据违反约束,FastAPI 会自动返回 422 状态码和一段错误详情。看一个具体例子。

假设 price 传了 -5,而模型要求 gt=0,你会收到:

{
    "detail": [
        {
            "type": "greater_than",
            "loc": ["body", "price"],
            "msg": "Input should be greater than 0",
            "input": -5,
            "ctx": { "gt": 0 }
        }
    ]
}

每个错误条目包含几个字段:

  • type:错误类型,比如 greater_than 表示「应大于某值」。
  • loc:出错位置,["body", "price"] 说明在请求体的 price 字段。
  • msg:人类可读的错误信息。
  • input:客户端实际传进来的值。
  • ctx:上下文,这里给出约束的具体数值 gt: 0

这份报错结构清晰,前端能直接拿去提示用户「价格必须大于 0」。

11-7 错误信息本地化的思路

默认情况下,FastAPI 返回的 msg 是英文的。如果你的用户主要是中文群体,可能希望提示也用中文。这里提供两条思路。

第一条,是用自定义校验抛出中文信息。借助 Pydantic v2 的 field_validator,你可以在校验失败时主动抛出带中文说明的异常:

from pydantic import BaseModel, field_validator

class Item(BaseModel):
    price: float

    @field_validator("price")
    @classmethod
    def check_price(cls, value: float) -> float:
        if value <= 0:
            raise ValueError("价格必须大于 0")
        return value

这样报错信息就会变成你写的中文。适合个别关键字段的定制。

第二条,是对整批错误做统一翻译。FastAPI 抛出的 RequestValidationError 里,每个错误都是结构化的(有 typelocctx)。你可以注册一个异常处理函数,根据 type 把英文 msg 映射成中文文案,再返回给客户端。这种方式适合需要全站中文化的项目。

Tip

全站翻译建议做成「错误类型 → 中文模板」的映射表,而不是逐字机翻。这样既能保证语气统一,也方便后续维护。具体实现会在「错误处理」相关章节展开。

11-8 小结

Field 是给请求体字段加约束的主力工具,记住这几点:

  • 它从 pydantic 导入,不是从 fastapi
  • 常用约束有 gtgeltlemin_lengthmax_length
  • Field(...) 表示必填,Field(None) 表示可选。
  • titledescriptionexamples 是元数据,会进文档但不参与校验。
  • 校验失败返回 422,报错里带 locmsg,定位问题很方便。
  • 想要中文提示,可用 field_validator 抛中文,或统一翻译 RequestValidationError

字段校验让接口更健壮。下一章我们看更复杂的场景:模型里嵌套模型、列表里装对象。