Appearance
请求体与校验
GET 请求通过 URL 传参数,适合查询。提交数据——创建文章、更新内容、登录——要把数据放在请求体里。FastAPI 用 Pydantic 模型定义请求体的结构:哪些字段、什么类型、哪些必填、有什么约束。
一、Pydantic 模型
Pydantic 是一个数据校验库。定义一个模型类,声明字段和类型,Pydantic 自动校验传入数据:
python
from pydantic import BaseModel
class ArticleCreate(BaseModel):
title: str # 必填,字符串
content: str # 必填,字符串
status: str = "draft" # 可选,默认 draftArticleCreate 就是一个继承了 BaseModel 的类。字段后面跟类型标注,带默认值的是可选字段。这个模型描述了"创建一篇文章时,请求体应该长什么样"。
二、接收 POST 请求体
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")
def create_article(article: ArticleCreate):
return {"title": article.title, "content": article.content, "status": article.status}article: ArticleCreate 这个参数,FastAPI 看到类型是 Pydantic 模型,就从请求体的 JSON 里取数据,按 ArticleCreate 的字段校验。
测试:
bash
curl -X POST http://localhost:8000/articles \
-H "Content-Type: application/json" \
-d '{"title": "新文章", "content": "正文"}'少传 status 不报错——有默认值。少传 title 会报 422:
json
{
"detail": [
{
"type": "missing",
"loc": ["body", "title"],
"msg": "Field required"
}
]
}422 是 FastAPI 的校验失败状态码,错误信息里写清楚了哪个字段出了什么问题。这个错误格式是自动的,不用手写校验逻辑。
三、字段约束
Pydantic 用 Field 给字段加约束——最小长度、最大长度、正则匹配等:
python
from pydantic import BaseModel, Field
class ArticleCreate(BaseModel):
title: str = Field(min_length=1, max_length=100, description="文章标题")
content: str = Field(min_length=1, description="正文内容")
status: str = Field(default="draft", pattern="^(draft|published)$")传 title 为空字符串,或者 status 传了 "archived"(不匹配正则),都返回 422 错误。
常用的约束:
| 约束 | 作用于 | 示例 |
|---|---|---|
min_length / max_length | 字符串 | 标题 1-100 字符 |
gt / ge / lt / le | 数字 | 端口 > 0,分页 limit ≤ 100 |
pattern | 字符串正则 | 状态只能是 draft 或 published |
default | 所有类型 | 不传时的默认值 |
这些约束直接体现在自动文档里——访问 /docs 时,每个字段的约束一目了然。
四、响应模型
默认情况下接口返回什么,FastAPI 就原样转成 JSON。但有时候想控制返回哪些字段——比如数据库实体里有密码字段,返回给前端时不该包含。
response_model 声明接口返回什么结构,FastAPI 按这个模型过滤多余字段:
python
class ArticleResponse(BaseModel):
id: int
title: str
status: str
# 不含 content——列表接口不需要返回正文
@app.get("/articles", response_model=list[ArticleResponse])
def list_articles():
return [
{"id": 1, "title": "Python 入门", "content": "很长的正文...", "status": "published"},
{"id": 2, "title": "Go 并发", "content": "也很长...", "status": "draft"},
]虽然函数返回的数据里有 content,但 response_model 声明了只返回 id、title、status,实际响应里 content 被过滤掉了。列表接口不返回大字段(正文)、详情接口才返回完整内容——这种区分用 response_model 控制。
五、嵌套模型
复杂的数据结构用嵌套模型表达。一篇文章带作者信息:
python
from pydantic import BaseModel
class Author(BaseModel):
name: str
email: str
class ArticleDetail(BaseModel):
id: int
title: str
status: str
author: Author # 嵌套模型
tags: list[str] # 字符串列表对应的 JSON:
json
{
"id": 1,
"title": "Python 入门",
"status": "published",
"author": {
"name": "张三",
"email": "zhangsan@example.com"
},
"tags": ["Python", "入门"]
}Pydantic 自动处理嵌套校验——author 里的 name 少了或类型不对,一样返回 422。
六、更新操作——部分字段可选
创建时所有字段必填,更新时只改部分字段。用两种模型区分:
python
from pydantic import BaseModel
from typing import Optional
class ArticleCreate(BaseModel):
title: str
content: str
status: str = "draft"
class ArticleUpdate(BaseModel):
title: Optional[str] = None # 全部可选,不传就是不改
content: Optional[str] = None
status: Optional[str] = None
@app.patch("/articles/{article_id}")
def update_article(article_id: int, article: ArticleUpdate):
# 只更新传了的字段
stored = {"id": article_id, "title": "原标题", "content": "原内容", "status": "draft"}
update_data = article.model_dump(exclude_unset=True) # 只拿传了的字段
stored.update(update_data)
return storedOptional[str] = None 让字段变成可选——不传时是 None。model_dump(exclude_unset=True) 只返回客户端实际传了的字段,没传的不包含——这样 PATCH 更新就只改传入的字段,不动其他字段。