首页 / FastAPI 入门教程 / 响应模型 Response Model

FastAPI 入门教程

响应模型 Response Model

本教程共 50 篇 · 第 14 篇 · 更新于 2026-08-12 · 约 8 分钟阅读

FastAPIFastAPI 入门教程response_model响应模型Pydantic数据过滤

本节目标:学会用 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 会替你做四件事:

  1. 校验返回的数据。如果返回对象缺字段或类型不对,说明是你的代码写错了,FastAPI 会返回服务器错误,而不是把错误数据发给客户端。
  2. 在 OpenAPI 里生成对应的 JSON Schema,自动文档 /docs 会显示它。
  3. 用 Pydantic 把数据序列化成 JSON(Pydantic 底层是 Rust 写的,速度很快)。
  4. 最关键的一点:把输出限制并过滤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 只有 usernameemail

Danger

永远不要把明文密码存库,也不要在响应里返回明文密码。上面的代码只是为了演示过滤效果,真实项目里密码必须哈希处理。

这种”输入含密码、输出不含密码”的写法,是保护用户隐私的基本功。如果偷懒用同一个模型同时当输入和输出,哪天你把用户列表发给前端,所有人的密码就一起泄露了。

14-3 用继承让类型提示更顺手

上面例子中,UserInUserOut 是两个完全独立的类。如果直接把函数返回类型标注成 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 实例。

编辑器不会报错,因为 UserInBaseUser 的子类,类型上是合法的。而 FastAPI 看到 response_model=BaseUser,依然只输出 usernameemail,把 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 没有设 descriptiontaxtags,返回的就只有真正给过的值:

{
    "name": "Foo",
    "price": 50.2
}

而请求 /items/bar,它显式设了 descriptiontax,这些就会正常返回。

再看 baz:它显式写了 tax=10.5,虽然这个值恰好等于模型默认值,但 FastAPI 能判断出这是你主动设的,照样会返回它。

14-5 临时增减字段:include 与 exclude

如果你只有一个模型,只是想临时少返回几个字段,可以用 response_model_includeresponse_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 适合临时裁剪,但长期维护仍推荐多模型方案。

把返回结构管好了,前端拿到的数据才干净、安全、可控。