FastAPI 响应模型指南:从 JSON 数据定义到动态管理的实践
本篇文章详细介绍了如何在 FastAPI 中使用响应模型,包括在路径操作函数中声明 response_model
、处理请求与响应数据不同时的场景,以及通过参数如 response_model_exclude_unset
来优化响应数据。文中还探讨了如何使用 response_model_include
和 response_model_exclude
参数动态控制响应字段的显示,帮助开发者轻松精简响应内容。通过丰富的代码示例和最佳实践建议,您将全面掌握如何定义和管理响应模型,从而确保接口数据的准确性和可维护性,为构建高质量 API 提供坚实基础。
文章目录
在 API 开发中,响应的数据结构直接影响客户端对接口的理解和使用体验。FastAPI 提供了强大的 response_model
功能,通过 Pydantic 模型严格定义接口的响应数据格式,使开发者能够更高效地管理数据结构。以下示例中使用的 Python 版本为 Python 3.10.15
,FastAPI 版本为 0.115.4
。
一 操作函数中声明响应模型
在 FastAPI 中,可以通过使用 response_model
参数来指定路径操作(如 get
、post
等方法)的响应模型。
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.post("/items01/", response_model=Item)
async def create_item(item: Item) -> Any:
return item
@app.get("/items02/", response_model=list[Item])
async def read_items() -> Any:
return [
{"name": "Portal Gun", "price": 42.0},
{"name": "Plumbus", "price": 32.0},
]
不同于 路径操作函数 的其他参数和请求体,response_model
直接作为装饰器方法的参数。此参数接受一个 Pydantic 模型或一个 Pydantic 模型的列表,例如 List[Item]
。这样设置后,响应数据将被限制在指定模型的结构内。运行代码文件 chapter15.py 来启动应用:
$ uvicorn chapter15:app --reload
在 SwaggerUI 中可以查看在线文档:http://127.0.0.1:8000/docs
。
二 请求与响应数据相同
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
@app.post("/user01/")
async def create_user(user: UserIn) -> UserIn:
return user
定义了一个包含明文密码的 UserIn
模型,用于处理输入和输出数据。这导致在通过浏览器提交密码创建用户时,API 也会在响应中包含密码。虽然 Pydantic 模型推荐复用,但在其他路径操作中使用相同模型可能会导致密码意外泄露给所有客户端。
划重点
永远不要存储或发送用户的明文密码。
三 请求和响应数据不同
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/user02/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user
可以创建一个包含明文密码的输入模型 UserIn
和一个不包含密码的输出模型 UserOut
。这样,即使路径操作函数返回了包含密码的输入模型,通过将 response_model
设置为 UserOut
,FastAPI 将利用 Pydantic 自动过滤掉输出模型中未声明的所有数据。
四 响应数据模型设置默认值
响应模型的数据可以设置默认值。
class Item01(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
tags: list[str] = []
items01 = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item01, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items01[item_id]
在 路径操作装饰器 中设置参数 response_model_exclude_unset=True
(推荐使用)以排除未显式设置的字段。这有助于确保响应 JSON 中只包含实际设定的字段值。
1 排除默认数据
原始响应 JSON :
{
"name": "Foo",
"description": null,
"price": 50.2,
"tax": 10.5,
"tags": []
}
未显式设置的字段在响应 JSON 中不包含:
{
"name": "Foo",
"price": 50.2
}
2 默认值字段有实际数据
看 bar 数据:
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
3 默认值与实际数据相同
看 baz 数据:
{
"name": "Baz",
"description": null,
"price": 50.2,
"tax": 10.5,
"tags": []
}
在 FastAPI 中,使用 response_model_exclude_unset=True
可有效控制哪些字段包含在响应中,帮助精简响应并避免发送无用或默认数据。此外,还有其他相关参数可以用于细化控制响应数据:
参数 | 描述 | 使用场景 |
---|---|---|
response_model_exclude_unset | 排除在输出模型中未显式设置的字段。 | 用于防止未显式设置的字段被包含在响应中。 |
response_model_exclude_defaults | 排除等于模型中默认值的字段。 | 用于避免包含未明确更改的默认值字段。 |
response_model_exclude_none | 排除在输出模型中设为 None 的字段。 | 用于从最终响应中省略明确设置为 None 的字段。 |
推荐使用:response_model_exclude_unset=True
,以确保响应中仅包含有意义的数据。
五 include/exclude 管理响应模型
FastAPI允许通过 response_model_include
和 response_model_exclude
参数来管理响应数据中包含或排除的字段。这些参数接收一个属性名称 str
组成的 set
,分别用于指定响应中 应包含的字段(排除其他字段)或 应排除的字段(包含其他字段)。
class Item02(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
items02 = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item02,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items02[item_id]
@app.get("/items/{item_id}/public", response_model=Item02, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
return items02[item_id]
@app.get(
"/items/{item_id}/name01",
response_model=Item02,
response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
return items02[item_id]
@app.get("/items/{item_id}/public01", response_model=Item02, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
return items02[item_id]
注:使用 response_model_include
或 response_model_exclude
参数来控制响应字段,生成的OpenAPI 定义和文档中的 JSON Schema 仍将显示完整的数据模型。
推荐使用不同的类来定义不同的响应模型,而不是依赖这些参数,这样做不仅可以清晰地管理数据结构,还可以确保 API 文档的 准确性 和 易用性。
使用列表或元组代替集合
@app.get(
"/items/{item_id}/name",
response_model=Item02,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items02[item_id]
@app.get(
"/items/{item_id}/name01",
response_model=Item02,
response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
return items02[item_id]
使用 {"name", "description"}
语法可以快速创建一个包含这两个元素的集合(set),这与使用 set(["name", "description"])
效果相同。如果误用 ["name", "description"]
列表(list)或元组(tuple)代替集合(set),FastAPI 会自动将其转换为集合,并且功能仍然正常运行。
六 完整代码示例
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.post("/items01/", response_model=Item)
async def create_item(item: Item) -> Any:
return item
@app.get("/items02/", response_model=list[Item])
async def read_items() -> Any:
return [
{"name": "Portal Gun", "price": 42.0},
{"name": "Plumbus", "price": 32.0},
]
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/user01/")
async def create_user(user: UserIn) -> UserIn:
return user
@app.post("/user02/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user
class Item01(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
tags: list[str] = []
items01 = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item01, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items01[item_id]
class Item02(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
items02 = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item02,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items02[item_id]
@app.get("/items/{item_id}/public", response_model=Item02, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
return items02[item_id]
@app.get(
"/items/{item_id}/name01",
response_model=Item02,
response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
return items02[item_id]
@app.get("/items/{item_id}/public01", response_model=Item02, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
return items02[item_id]
七 源码地址
八 参考
[1] FastAPI 文档
标签:指南,name,FastAPI,id,item,JSON,str,model,response From: https://blog.csdn.net/u014394049/article/details/144187554