FastAPI 新手入门第 4 篇:用响应模型控制接口返回字段
上一篇我们用 ItemCreate 接收 JSON 请求体,解决了“前端传什么进来”的问题。
这一篇看另一边:接口要返回什么给前端。
做完后,我们会让 POST /items 在函数里生成一个内部字段 internal_note,但浏览器最终拿到的 JSON 里看不到它。这个效果就是响应模型带来的。
我会继续将代码放到 fastapi-beginner-lab 仓库中,有需要的朋友自取。
这次要改哪几行
前几篇已经写过的启动代码和 GET 接口不再重复贴。这次只看新增和改动的部分。
这段代码重点看 ItemPublic 和 response_model=ItemPublic。前者说明响应允许有哪些字段,后者把这个说明挂到 POST /items 上。
class ItemPublic(BaseModel):
id: int
name: str
price: float
is_offer: bool = False
@app.post("/items", response_model=ItemPublic)
def create_item(item: ItemCreate):
item_data = item.model_dump()
return {
"id": 1,
**item_data,
"internal_note": "Only visible inside the server.",
}
这里函数返回的字典里有 internal_note,但 ItemPublic 里没有这个字段。
我们先跑起来看结果。
在 /docs 里调用一次
启动服务:
.\.venv\Scripts\Activate.ps1
$env:PYTHONIOENCODING = "utf-8"
$env:PYTHONUTF8 = "1"
fastapi dev app/main.py
打开:
http://127.0.0.1:8000/docs
点开 POST /items,请求体填入:
{
"name": "Notebook",
"price": 12.5,
"is_offer": true
}
点 Execute 后,应该能看到:
{
"id": 1,
"name": "Notebook",
"price": 12.5,
"is_offer": true
}
注意这里没有 internal_note。
函数明明返回了这个字段,响应里却没有它。FastAPI 按 ItemPublic 把返回值过滤了一遍。
为什么请求模型和响应模型要分开
第 3 篇里我们写了 ItemCreate:
class ItemCreate(BaseModel):
name: str
price: float
is_offer: bool = False
它描述的是创建商品时,客户端需要提交什么。
这一篇加的 ItemPublic 描述的是创建成功后,接口准备返回什么。
class ItemPublic(BaseModel):
id: int
name: str
price: float
is_offer: bool = False
这两个模型看起来很像,但用途不同。
ItemCreate 不需要 id,因为创建时 ID 通常由服务端生成。
ItemPublic 需要 id,因为创建成功后前端通常要知道新商品的 ID。
真实项目里还会有更多差异。比如服务端内部可能保存 internal_note、cost_price、created_by 这类字段,但它们不一定适合返回给前端。
response_model 做了什么
response_model 写在接口装饰器上:
@app.post("/items", response_model=ItemPublic)
它告诉 FastAPI:这个接口返回给客户端的数据,要按 ItemPublic 来处理。
这会带来三个直接效果:
- 响应字段会被过滤,只保留
ItemPublic里声明过的字段。 /docs里会显示这个接口的响应结构。- 如果函数返回的数据缺少
ItemPublic要求的字段,服务端会报错,提醒我们代码返回了不符合约定的数据。
现在最值得记住的是第一点:响应模型能挡住不该出现在响应里的字段。
看一下内部字段怎么被过滤
我们返回的是这个字典:
return {
"id": 1,
**item_data,
"internal_note": "Only visible inside the server.",
}
如果没有 response_model=ItemPublic,这个接口会把 internal_note 一起返回。
加上响应模型后,FastAPI 会按 ItemPublic 重新整理响应:
函数返回 dict -> 按 ItemPublic 过滤字段 -> 转成 JSON 响应
所以浏览器看到的是:
{
"id": 1,
"name": "Notebook",
"price": 12.5,
"is_offer": true
}
这一步很适合新手早点养成习惯。接口返回值不是临时拼一个 dict 就结束了,它也是前端会依赖的约定。
在 /docs 里看响应结构
回到 /docs,点开 POST /items。
除了请求体,你还能看到响应区域里多了 ItemPublic 对应的结构。
这说明 FastAPI 不只是运行时过滤字段,还会把响应格式写进接口文档。前端看文档时,就知道这个接口会返回 id、name、price 和 is_offer。
现在我们还在单文件示例里写代码,感觉不明显。等接口多起来后,这种明确的响应结构会减少很多沟通成本。
动手改一下
现在给函数内部返回值加一个成本价字段:
@app.post("/items", response_model=ItemPublic)
def create_item(item: ItemCreate):
item_data = item.model_dump()
return {
"id": 1,
**item_data,
"internal_note": "Only visible inside the server.",
"cost_price": 8.0,
}
保存后回到 /docs,再次调用 POST /items。
如果响应里看不到 cost_price,说明 response_model=ItemPublic 仍然在工作。
到这里,这篇的目标已经完成:
- 我们用
ItemPublic描述了接口返回给前端的数据。 - 我们让
POST /items使用了response_model=ItemPublic。 - 我们确认了函数内部返回的
internal_note不会出现在最终响应里。
下一篇继续处理接口失败的情况:资源不存在时,应该返回 404,而不是随手返回一段字符串。
更多推荐



所有评论(0)