响应模型 Response Model
本教程共 50 篇 · 第 14 篇 · 更新于 2026-08-12 · 约 8 分钟阅读
本节目标:学会用
response_model控制接口返回哪些字段,避免把密码等敏感数据发给前端,并能按需裁剪输出。
前面我们写的接口,函数 return 什么,客户端就拿到了什么。这在简单场景下没问题。可一旦涉及真实业务,麻烦就来了:你存了用户的密码、身份证号、内部标记,难道要把它们原样返回给浏览器吗?显然不行。
FastAPI 提供了一个非常实用的开关——response_model,专门用来声明”接口对外返回的数据结构”。用了它之后,FastAPI 会自动按模型过滤,只把模型里声明的字段吐出去,没声明的字段一律丢弃。
14-1 用 response_model 声明返回结构
response_model 是路径操作装饰器的参数,写在 @app.get()、@app.post() 这类装饰器里,不要写成函数的参数。
它接收的类型,和你在函数参数里声明 Pydantic 模型、列表、字典、标量值(整数、布尔等)是同一种写法。最常用的是传一个 Pydantic 模型。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
is_active: bool
@app.get("/users/me", response_model=User)
async def read_user_me() -> User:
return User(id=1, name="小明", is_active=True)
当你声明了 response_model=User,FastAPI 会替你做四件事:
- 校验返回的数据。如果返回对象缺字段或类型不对,说明是你的代码写错了,FastAPI 会返回服务器错误,而不是把错误数据发给客户端。
- 在 OpenAPI 里生成对应的 JSON Schema,自动文档
/docs会显示它。 - 用 Pydantic 把数据序列化成 JSON(Pydantic 底层是 Rust 写的,速度很快)。
- 最关键的一点:把输出限制并过滤成
response_model里定义的字段。
Note
response_model是装饰器的参数,不是路径操作函数的参数。它和status_code一样,写在@app.xxx(...)的圆括号里。
14-2 输入模型和输出模型分开
最常见的需求,是把”接收的数据”和”返回的数据”分开。比如注册用户时,客户端要提交密码;但你绝不能把密码再原样返回。
做法很简单:写一个带密码的输入模型,再写一个不带密码的输出模型。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: str
class UserOut(BaseModel):
username: str
email: str
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn) -> UserOut:
# 这里通常会把 user.password 做哈希后存库
return user
注意看:函数 return user 返回的是完整的 UserIn,里面包含 password。但因为 response_model=UserOut,FastAPI 会自动把 password 过滤掉。客户端拿到的 JSON 只有 username 和 email。
Danger永远不要把明文密码存库,也不要在响应里返回明文密码。上面的代码只是为了演示过滤效果,真实项目里密码必须哈希处理。
这种”输入含密码、输出不含密码”的写法,是保护用户隐私的基本功。如果偷懒用同一个模型同时当输入和输出,哪天你把用户列表发给前端,所有人的密码就一起泄露了。
14-3 用继承让类型提示更顺手
上面例子中,UserIn 和 UserOut 是两个完全独立的类。如果直接把函数返回类型标注成 UserOut,编辑器会报错:你返回的是 UserIn,类型对不上。
解决办法是用继承。把公共字段放在基类,密码放在子类:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class BaseUser(BaseModel):
username: str
email: str
class UserIn(BaseUser):
password: str
@app.post("/users/", response_model=BaseUser)
async def create_user(user: UserIn) -> BaseUser:
return user
这段代码里,UserIn 拥有 BaseUser 的全部字段,再多一个 password。函数返回类型标成 BaseUser,实际返回 UserIn 实例。
编辑器不会报错,因为 UserIn 是 BaseUser 的子类,类型上是合法的。而 FastAPI 看到 response_model=BaseUser,依然只输出 username 和 email,把 password 过滤掉。
这样你既拿到了编辑器的类型检查支持,又享受了 FastAPI 的字段过滤,两全其美。
Tip如果你只想临时返回”任意类型”,又想用
response_model做过滤,可以把函数返回类型写成Any(需要从typing导入)。但这属于进阶用法,初学先掌握继承即可。
14-4 只返回真正设置过的值
有时候你的模型有很多带默认值的字段。比如一个商品,描述、税率、标签都有默认值。如果每次都把所有默认值原样返回,JSON 会又长又啰嗦。
这时候用 response_model_exclude_unset=True,FastAPI 就只返回你显式设置过的字段,默认值一律不输出。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
tags: list[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "调酒师专用", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "price": 50.2, "tax": 10.5},
}
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str) -> Item:
return items[item_id]
请求 /items/foo,因为 foo 没有设 description、tax、tags,返回的就只有真正给过的值:
{
"name": "Foo",
"price": 50.2
}
而请求 /items/bar,它显式设了 description 和 tax,这些就会正常返回。
再看 baz:它显式写了 tax=10.5,虽然这个值恰好等于模型默认值,但 FastAPI 能判断出这是你主动设的,照样会返回它。
14-5 临时增减字段:include 与 exclude
如果你只有一个模型,只是想临时少返回几个字段,可以用 response_model_include 或 response_model_exclude。它们接收一个字段名的集合(set)。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
@app.get(
"/items/{item_id}",
response_model=Item,
response_model_exclude={"tax"},
)
async def read_item(item_id: str) -> Item:
return Item(name="Foo", price=50.2)
上面的写法会让返回的 JSON 里始终没有 tax 字段。
Tip用大括号
{"name", "description"}创建集合,等价于set(["name", "description"])。即使你不小心传了列表或元组,FastAPI 也会自动转成集合,不影响使用。
不过要提醒一句:这两个参数只是运行时过滤,OpenAPI 文档里生成的仍然是完整模型(include/exclude 不会改变 Schema 结构),它们只影响实际返回的 JSON 字段。所以官方更推荐用”多个模型 + 继承”的方式(见 14-2、14-3),而不是依赖 include/exclude。结构清晰、文档准确,才是长久之计。
14-6 小结
response_model 是你控制接口输出边界的核心工具:
- 用
response_model=模型声明返回结构,FastAPI 自动过滤未声明的字段。 - 把输入模型和输出模型分开,是保护密码等敏感信息的基本手段。
- 用继承(基类 + 子类)既能让编辑器类型提示正常,又能过滤多余字段。
response_model_exclude_unset=True只返回客户端真正设置过的值,省流量也更干净。response_model_include/response_model_exclude适合临时裁剪,但长期维护仍推荐多模型方案。
把返回结构管好了,前端拿到的数据才干净、安全、可控。