🐸 青蛙编程网
首页 / Python / FastAPI / 4. 请求体与 Pydantic

请求体与 Pydantic

这是什么:请求体(body)= 客户端随 POST/PUT 请求发来的数据本体(通常是一段 JSON);Pydantic 是 FastAPI 背后的数据校验库,用 BaseModel 类把「数据长什么样」声明出来,校验全自动。

为什么用:GET 只能带几个查询参数,创建待办、提交订单这种「一整块数据」必须走请求体。手写校验「字段在不在、类型对不对」又烦又漏——用 BaseModel 声明一次,校验、转换、文档三件事一起解决。

如何用:写一个继承 BaseModel 的类,把它标成路由函数的参数,FastAPI 自动把请求 JSON 变成对象。

定义模型:一个类描述一份数据

from pydantic import BaseModel

class Todo(BaseModel):
    title: str
    done: bool = False
行 含义
title: str 必填字段:请求里必须有 title,且必须是字符串
done: bool = False 可选字段:不传就用默认值 False

这就是面向对象里学的类派上了新用场:类不是用来继承复用的,是用来描述数据形状的。

接收请求体:模型当参数

from fastapi import FastAPI

app = FastAPI()

@app.post("/todos")
def create_todo(todo: Todo):
    return {"created": todo.title, "done": todo.done}

参数 todo: Todo 不带默认值、又不是基础类型,FastAPI 就把它当请求体处理:把客户端发来的 JSON 按模型解析成 Todo 对象,todo.title 直接用属性访问。

启动后用 /docs 试一下(也可以用 curl / Postman),请求体:

{
  "title": "学完 FastAPI",
  "done": false
}

响应:

{
  "created": "学完 FastAPI",
  "done": false
}

自动校验:错一点也不放过

少传必填字段、类型给错,都会被 422 拦下,且明细精确到字段:

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "title"],
      "msg": "Field required"
    }
  ]
}

loc 里的 body.title 直接指出「请求体里缺 title」。对比 Flask 里手写 data.get("title") 逐个检查,这套校验零成本且不会漏。类型不对时还会自动转换:JSON 里给 "done": "true"(字符串),能安全转成 True 就转,转不了才报错。

Field:给字段加规则

类型对了还不够?Field 加约束:

from pydantic import BaseModel, Field

class Todo(BaseModel):
    title: str = Field(min_length=1, max_length=50)
    priority: int = Field(default=1, ge=1, le=5)   # 1~5 级

title 不能是空串、超 50 字符报错;priority 必须在 1 到 5 之间(ge = 大于等于,le = 小于等于)。规则写在类里,接口、文档、校验三处同时生效。

完整小示例:能收能回的小接口

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class Todo(BaseModel):
    title: str = Field(min_length=1, max_length=50)
    done: bool = False

todos: list[Todo] = []          # 先放内存里,下一章换数据库

@app.post("/todos")
def create_todo(todo: Todo):
    todos.append(todo)
    return {"count": len(todos), "latest": todo.title}

@app.get("/todos")
def list_todos():
    return {"total": len(todos), "items": [t.model_dump() for t in todos]}

逐段中文精讲

  • 模型区:Todo 声明数据形状,title 必填且 1~50 字,done 默认 False。
  • todos: list[Todo]:模块级列表当「临时仓库」,服务重启就清空——下章换 SQLite 解决。
  • POST 路由:todo: Todo 自动完成「解析 + 校验」,能走到函数体说明数据合法,直接入仓。
  • t.model_dump():Pydantic v2 的「对象转 dict」方法,方便序列化回 JSON 返回。
  • 跑起来后在 /docs 里连发两次 POST,再 GET 一次,你会看到 total 变成 2。

记忆口诀:继承 BaseModel,字段即规则;少传错类不用怕,422 明细帮你查。

📝 本章小结

  • 请求体 = POST/PUT 携带的 JSON 数据;带模型注解的参数自动从 body 解析
  • BaseModel 类声明数据形状:必填靠无默认值,可选靠默认值
  • 校验失败统一 422,loc 精确到字段;Field 可加长度、范围等约束
  • /docs 里点「Try it out」就能填 JSON 调试,替代一多半手动测试

动手改

  1. 给 Todo 加 deadline: str | None = None 字段(可选截止日期),重看 /docs 里的示例
  2. POST 一个缺 title 的请求,把 422 的 loc 抄下来
  3. 把 priority 约束改成 ge=1, le=3,传 4 试试会发生什么

上一章:路由与参数 · 下一章:响应与错误处理 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。