← 返回题目列表

FastAPI 如何利用 Pydantic 做参数校验和响应序列化?

高频 中等 第 8 / 27 题 更新于 2026/07/27
Pydantic数据校验response_model序列化

简化版

FastAPI 通过 Pydantic 模型和类型注解自动校验请求数据,并把校验失败转换成 422 响应。返回数据时,可以用 response_model 控制响应结构、过滤字段、做序列化,让接口输入输出都有明确契约。

详细版

FastAPI 的请求校验主要依赖 Python 类型注解和 Pydantic:

  • 路径参数、查询参数根据函数参数类型校验;
  • 请求体通常用 Pydantic BaseModel 定义;
  • 字段约束可以用 FieldQueryPathBody 等声明;
  • 校验失败时 FastAPI 自动返回结构化错误;
  • response_model 可以约束返回结构,避免泄漏敏感字段。

示例:

from pydantic import BaseModel, Field

class UserCreate(BaseModel):
    name: str = Field(min_length=2, max_length=30)
    age: int = Field(ge=0, le=150)

@app.post("/users", response_model=UserOut)
def create_user(user: UserCreate):
    return save_user(user)

面试重点是:Pydantic 不只是类型提示,它参与运行时校验和序列化。

完整版教学

一、类型注解在 FastAPI 中不是装饰品

在普通 Python 代码里,类型注解很多时候只用于静态检查和 IDE 提示,运行时不会自动强制类型。但在 FastAPI 中,类型注解会被框架读取,用来决定参数来自哪里、如何校验、如何生成文档。

比如:

@app.get("/items/{item_id}")
def get_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

item_id: int 表示路径参数必须能转换成整数。如果用户访问 /items/abc,FastAPI 会返回校验错误,而不是让业务代码自己判断。q 因为有默认值 None,所以是可选查询参数。

这就是 FastAPI 的核心体验:函数签名本身就是接口契约。

二、请求体为什么通常用 Pydantic 模型

当请求体是 JSON 对象时,一般用 Pydantic 模型表达结构:

from pydantic import BaseModel, EmailStr

class UserCreate(BaseModel):
    username: str
    email: EmailStr
    password: str

这个模型会告诉 FastAPI:请求体必须包含 usernameemailpassword,并且 email 要符合邮箱格式。校验通过后,路由函数拿到的是一个 UserCreate 对象,而不是原始 dict。

这样写有三个好处。第一,业务代码更干净,不需要反复从字典里取值。第二,字段约束集中在模型里,便于复用。第三,OpenAPI 文档会自动展示请求体 schema。

三、Field、Query、Path 的作用

Pydantic 的 Field 常用于模型字段约束:

class ProductCreate(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    price: float = Field(gt=0)

FastAPI 的 QueryPathBody 则用于声明请求参数来源和约束:

from fastapi import Query, Path

@app.get("/products/{product_id}")
def get_product(
    product_id: int = Path(gt=0),
    keyword: str | None = Query(default=None, max_length=50),
):
    return {"product_id": product_id, "keyword": keyword}

这些声明不仅用于运行时校验,也会进入接口文档。面试时可以强调:FastAPI 的参数声明同时服务于校验、文档和可读性。

四、422 错误说明了什么

FastAPI 参数校验失败时,通常返回 HTTP 422。422 表示请求格式语法上可解析,但语义上不符合接口要求。例如 JSON 是合法的,但字段类型不对、必填字段缺失、数值超出范围。

这和 400 有区别。400 更偏向请求本身不合法,比如 JSON 语法错误。FastAPI 默认用 422 表达校验失败,这是符合很多 API 设计习惯的。

返回的错误结构会指出错误位置、错误类型和错误信息。前端或调用方可以根据这些信息定位字段问题。

五、response_model 的价值

很多人只关注请求校验,忽略响应序列化。事实上 response_model 非常重要:

class UserOut(BaseModel):
    id: int
    username: str
    email: str

@app.get("/users/{user_id}", response_model=UserOut)
def get_user(user_id: int):
    return {
        "id": user_id,
        "username": "tom",
        "email": "tom@example.com",
        "password_hash": "secret",
    }

即使返回对象里有 password_hash,响应也会按 UserOut 过滤掉。这能降低敏感字段泄漏风险。

response_model 还可以把 ORM 对象、字典或 Pydantic 对象统一序列化成符合 schema 的响应。对于大型项目,输入模型、输出模型、数据库模型通常要分开,不要直接把数据库模型暴露给外部。

六、Pydantic v1 和 v2 要注意差异

FastAPI 在较新版本中支持 Pydantic v2。Pydantic v2 在配置方式、校验器写法、序列化方法上和 v1 有差异。例如 v1 常见 orm_mode = True,v2 中更常见 model_config = ConfigDict(from_attributes=True)

面试不一定深挖版本细节,但工程上要注意项目使用的 FastAPI 和 Pydantic 版本。复制旧代码时,如果版本不一致,可能出现模型配置失效、校验器不运行等问题。

七、常见误区与追问

场景推荐做法说明
创建用户UserCreate 输入模型包含密码等写入字段
返回用户UserOut 响应模型不包含 password_hash、内部状态
数据库存储ORM Model负责表结构和持久化,不直接作为外部契约
class UserCreate(BaseModel):
    email: EmailStr
    password: str = Field(min_length=8)

class UserOut(BaseModel):
    id: int
    email: EmailStr

@app.post("/users", response_model=UserOut)
def create_user(user: UserCreate):
    saved = save_user(user)
    return saved

记忆钩子:Pydantic 在 FastAPI 里负责“入口校验、出口塑形”,不是负责“落库保存”。

第一个误区是把 Pydantic 当 ORM。Pydantic 是数据校验和序列化工具,不负责数据库持久化。

第二个误区是输出直接返回数据库实体。这样容易泄漏内部字段,也让外部 API 和数据库结构强耦合。

第三个误区是过度依赖自动类型转换。例如字符串 "123" 能被转成整数,但对于一些严格业务场景,可能需要更明确的校验规则。

  • 误区:把 Pydantic 当 ORM。 Pydantic 做校验、转换和序列化,不管理连接、事务、查询和持久化。
  • 误区:直接把 ORM 实体作为对外响应。 ORM 模型可能包含密码哈希、删除标记、内部字段,应该用 response_model 单独定义输出契约。
  • 误区:只依赖宽松类型转换。 "123" 可以被转成整数,但金额、权限、枚举、业务状态往往需要 Field、枚举类型或自定义校验器明确约束。
  • 追问:422 和 400 怎么区分? 422 常表示请求语法可解析但字段语义不符合模型约束;400 更偏向请求本身格式错误或通用客户端错误。
  • 追问:Pydantic v1 到 v2 常见迁移点是什么? v2 更常用 model_dump()model_validate()ConfigDict(from_attributes=True),旧的 dict()parse_obj()orm_mode 代码要结合版本核对。
  • 追问:为什么输入模型和输出模型要分开? 输入关注客户端能提交什么,输出关注服务端允许暴露什么,二者字段、安全边界和演进节奏都不同。

八、加强记忆

记 Pydantic 在 FastAPI 里的作用,可以抓住“入口校验、出口塑形”这六个字:请求进来时按模型和类型注解校验,响应出去时按 response_model 序列化和过滤。它让接口契约集中在代码里,同时自动生成文档。