Appearance
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,定义了三个字段:
| 字段 | 类型 | 默认值 | 必填性 |
|---|---|---|---|
title | str | 无 | 必填 |
content | str | 无 | 必填 |
status | str | "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] | T 或 None,等价于 `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 = NoneArticleUpdate 中所有字段都是可选的,支持部分更新。
响应模型排除字段
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 或 NoneOptional[str] 只表示类型可以是 str 或 None,不代表默认值为 None。要让它可选,必须显式设置默认值 = None。