Skip to content

29|请求体与 Pydantic 基础

GET 请求通过 URL 传参数,但创建和更新资源时需要传递更多信息:文章标题和内容、服务器配置、用户资料。这些信息放在请求体(request body)中,通过 POST/PUT/PATCH 发送。FastAPI 用 Pydantic 模型定义请求体的结构,自动完成解析、校验和文档生成。

一、定义请求体模型

python
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class ArticleCreate(BaseModel):
    title: str
    content: str
    status: str = "draft"

@app.post("/articles")
async def create_article(article: ArticleCreate):
    return {
        "id": 1,
        "title": article.title,
        "content": article.content,
        "status": article.status,
    }

ArticleCreate 继承自 BaseModel,定义了三个字段:

字段类型默认值必填性
titlestr必填
contentstr必填
statusstr"draft"可选

客户端发送 JSON 请求体:

bash
curl -X POST http://localhost:8000/articles \
  -H "Content-Type: application/json" \
  -d '{"title": "入门", "content": "FastAPI 基础"}'

FastAPI 自动把 JSON 转成 ArticleCreate 对象。如果缺少必填字段或类型不匹配,返回 422 错误:

json
{
  "detail": [
    {
      "loc": ["body", "title"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}

二、字段类型

Pydantic 支持 Python 标准类型:

python
from pydantic import BaseModel
from typing import Optional, List

class ServerCreate(BaseModel):
    hostname: str                    # 必填字符串
    port: int                        # 必填整数
    active: bool = True              # 布尔值,默认 True
    tags: List[str] = []             # 字符串列表,默认空列表
    description: Optional[str] = None  # 可选字符串
类型说明
str字符串
int整数
float浮点数
bool布尔值
List[T]T 类型的列表
Optional[T]TNone,等价于 `T

三、路径参数 + 查询参数 + 请求体混合

一个接口可以同时使用三种参数:

python
@app.put("/articles/{article_id}")
async def update_article(
    article_id: int,                    # 路径参数
    article: ArticleUpdate,             # 请求体
    notify: bool = False,               # 查询参数
):
    return {
        "id": article_id,
        "title": article.title,
        "notify": notify,
    }

FastAPI 根据参数类型自动区分:

  • 路径中有 {} 的是路径参数
  • 类型为 Pydantic 模型的是请求体
  • 其他有默认值的是查询参数

四、请求体嵌套

请求体的字段也可以是另一个模型:

python
class Author(BaseModel):
    name: str
    email: str

class ArticleCreate(BaseModel):
    title: str
    content: str
    author: Author

请求 JSON:

json
{
  "title": "入门",
  "content": "FastAPI 基础",
  "author": {
    "name": "张三",
    "email": "zhangsan@example.com"
  }
}

五、排除和包含字段

请求体只用部分字段

创建和更新通常需要不同的字段集合:

python
class ArticleBase(BaseModel):
    title: str
    content: str

class ArticleCreate(ArticleBase):
    pass

class ArticleUpdate(ArticleBase):
    title: str | None = None
    content: str | None = None

ArticleUpdate 中所有字段都是可选的,支持部分更新。

响应模型排除字段

python
class UserInDB(BaseModel):
    id: int
    name: str
    email: str
    password_hash: str   # 敏感字段,不返回给客户端

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

@app.get("/users/{id}", response_model=UserOut)
async def get_user(id: int):
    user = get_user_from_db(id)   # 返回 UserInDB 形状的数据
    return user                   # password_hash 被自动过滤

六、常见错误

请求体 Content-Type 不对

bash
# 错误:缺少 Content-Type,FastAPI 无法解析请求体
curl -X POST http://localhost:8000/articles \
  -d '{"title": "test"}'

# 正确
curl -X POST http://localhost:8000/articles \
  -H "Content-Type: application/json" \
  -d '{"title": "test"}'

在 GET 请求中用请求体

python
# 不推荐:GET 请求语义上不应该有请求体,部分客户端/代理不支持
@app.get("/articles")
async def search(filter: ArticleFilter):
    ...

# 正确:GET 用查询参数,POST/PUT/PATCH 用请求体
@app.get("/articles")
async def search(q: str = "", status: str = ""):
    ...

Optional 和默认值混淆

python
# 这两行等价:都是可选且默认 None
email: Optional[str] = None
email: str | None = None

# 错误理解:以为 Optional[str] 默认就是 None
email: Optional[str]    # 必填!只是值可以是 str 或 None

Optional[str] 只表示类型可以是 strNone,不代表默认值为 None。要让它可选,必须显式设置默认值 = None