首页
/ FastAPI 中在 OpenAPI 里声明额外 Responses:用 responses 参数完整描述状态码、媒体类型与响应 Schema

FastAPI 中在 OpenAPI 里声明额外 Responses:用 responses 参数完整描述状态码、媒体类型与响应 Schema

2026-09-07 15:03:02作者:秋泉律Samson

导读:本文以 FastAPI 官方文档《OpenAPI 中的额外 Responses》(additional-responses)为主体,系统讲解如何通过 path operation decoratorresponses 参数声明额外的状态码、媒体类型、描述与示例,让 API 文档完整反映接口的真实行为。读完本文,你可以掌握 model 键的用法、多媒体类型响应声明、response_modelresponses 的信息合并机制,以及基于 **dict 解包复用预定义响应的实战技巧,并能从源码层面理解 FastAPI 是如何把这些声明转换为 OpenAPI Schema 的。

FastAPI 额外 Responses 在交互式 API 文档中的展示效果

1. 核心概念:为什么需要声明额外的 Responses

在 OpenAPI 中,一个接口的 responses 字段用来描述该接口所有可能的响应——不只是成功的 200,还包括 404403、重定向 302 等额外状态码。

通过 FastAPI 的 responses 参数声明的额外 responses,会直接进入生成的 OpenAPI schema,因此也会自动显示在交互式 API 文档(/docs)中。但有一个关键前提,官方文档用加粗的警告强调过:

  • 这些额外 responses 只是对文档和 Schema 的声明,FastAPI 不会替你自动生成这些响应;
  • 对于声明的每个额外 response,你需要自己在 endpoint 中显式返回一个 Response 实例(如 JSONResponseFileResponse),并携带对应的 status code 和 content。

这一警告同样体现在 additional-responses 文档开头的注意事项中,是一个相当 advanced 的主题——初学 FastAPI 时未必用得上,但在描述复杂接口行为时非常有用。

2. 用 model 键声明带 Pydantic 模型的额外 Response

path operation decorator(如 @app.get(...))传入 responses 参数。从源码看,该参数最终保存在 APIRoute 上:APIRoute 字段定义路由注册时的赋值 均为 responses: dict[int | str, dict[str, Any]],其中:

  • keys 是每个 response 的 status code(可以是整数如 404,也可以是字符串如 "default");
  • values 是描述该 response 信息的 dict

每个 response dict 中可以包含一个 model 键,其值是一个 Pydantic model,用法与 response_model 类似。FastAPI 会取出这个 model、为它生成 JSON Schema,并将其放到 OpenAPI 的合适位置

以声明一个带 404 状态码和 Message 模型的额外 response 为例(完整示例见 tutorial001_py310.py):

from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel


class Item(BaseModel):
    id: str
    value: str


class Message(BaseModel):
    message: str


app = FastAPI()


@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
    if item_id == "foo":
        return {"id": "foo", "value": "there goes my hero"}
    return JSONResponse(status_code=404, content={"message": "Item not found"})

注意最后两行:成功时返回普通 dict(由 response_model=Item 序列化),失败时必须直接返回 JSONResponse(status_code=404, ...)——这正是第 1 节警告的具体体现。

2.1 model 键不是 OpenAPI 的一部分

官方文档特别注明:model不属于 OpenAPI 规范,它只是 FastAPI 的扩展语法糖。FastAPI 会从该键取出 Pydantic model、生成 JSON Schema,并把它放到正确的结构中,即:

  • content 键(值是一个 dict),其中包含:
    • 一个 media type 键,如 application/json(值又是一个 dict),其中包含:
      • schema 键,其值就是 model 的 JSON Schema——这才是"正确的位置"。
        • 而且 FastAPI 不会内联展开该 Schema,而是在全局 JSON Schemas(components.schemas)中生成定义,然后在此处插入一个 $ref 引用。这样做的好处是:其他应用和客户端工具可以直接复用这些 Schema,更好的代码生成工具也能据此生成类型化的客户端代码。

这段逻辑的实现位于 fastapi/openapi/utils.py:在遍历 route.responses 时,FastAPI 会先 pop("model")model 键剥离,再通过 get_schema_from_model_field 生成 schema 并以 $ref 形式挂载到对应 media type 下(详见第 4 节源码剖析)。

2.2 生成的 OpenAPI responses 长什么样

针对上述 path operation,FastAPI 在 OpenAPI 中生成的 responses 为(注意 404200 中的 schema 都是全局引用):

{
    "responses": {
        "404": {
            "description": "Additional Response",
            "content": {
                "application/json": {
                    "schema": {
                        "$ref": "#/components/schemas/Message"
                    }
                }
            }
        },
        "200": {
            "description": "Successful Response",
            "content": {
                "application/json": {
                    "schema": {
                        "$ref": "#/components/schemas/Item"
                    }
                }
            }
        },
        "422": {
            "description": "Validation Error",
            "content": {
                "application/json": {
                    "schema": {
                        "$ref": "#/components/schemas/HTTPValidationError"
                    }
                }
            }
        }
    }
}

其中两个细节值得注意:

  • 当你在 responses 中只给了 {"model": Message} 而没有给 description 时,FastAPI 会自动补一个默认的 "Additional Response" 描述(源码中的回退链为:显式 description → 已存在的 description → 状态码标准文本 → 兜底文案 "Additional Response",见 utils.py 描述回退逻辑);
  • 由于该路径操作带有请求参数,FastAPI 还会自动补上 422(Validation Error)响应,除非你已经声明了 4224XXdefault 之一(见 utils.py 422 自动注入逻辑)。

对应的全局 Schemas 定义(components.schemas)为:

{
    "components": {
        "schemas": {
            "Message": {
                "title": "Message",
                "required": [
                    "message"
                ],
                "type": "object",
                "properties": {
                    "message": {
                        "title": "Message",
                        "type": "string"
                    }
                }
            },
            "Item": {
                "title": "Item",
                "required": [
                    "id",
                    "value"
                ],
                "type": "object",
                "properties": {
                    "id": {
                        "title": "Id",
                        "type": "string"
                    },
                    "value": {
                        "title": "Value",
                        "type": "string"
                    }
                }
            },
            "ValidationError": {
                "title": "ValidationError",
                "required": [
                    "loc",
                    "msg",
                    "type"
                ],
                "type": "object",
                "properties": {
                    "loc": {
                        "title": "Location",
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "msg": {
                        "title": "Message",
                        "type": "string"
                    },
                    "type": {
                        "title": "Error Type",
                        "type": "string"
                    }
                }
            },
            "HTTPValidationError": {
                "title": "HTTPValidationError",
                "type": "object",
                "properties": {
                    "detail": {
                        "title": "Detail",
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ValidationError"
                        }
                    }
                }
            }
        }
    }
}

3. 源码剖析:model 键是如何被处理的

注册路由时,fastapi/routing.py 会遍历 route.responses

  • 先断言每个 value 必须是 dict"An additional response must be a dict");
  • 若其中包含 model,则断言该 status code 允许携带响应体(例如 204304 这类无 body 的状态码不能用 model),随后用 create_model_field 为该模型创建一个名为 Response_{status_code}_{route.unique_id} 的序列化字段,存入 route.response_fields
  • 生成 OpenAPI 时(fastapi/openapi/utils.py),FastAPI 对 route.responses 逐项 copy.deepcopypop("model") 后:
    • 把 status code 转为字符串键并大写;"DEFAULT" 会被特殊处理为小写 "default"(这是 OpenAPI 中表示"其他所有状态码"的合法键);
    • 若该状态码在 route.response_fields 中有对应字段(即声明了 model),则生成 schema 并 deep_dict_update 合并进 content.{media_type}.schema;media type 的取值为路由响应类的 media type,若无则回退为 application/json
    • 最后用 deep_dict_update 把处理后的 response dict 深合并进 operation.responses 中对应状态码的位置,并保证 description 始终存在。

这也解释了第 2 节两个 JSON 示例中 $ref 的来源:Message 模型被注册为全局组件,404 响应只持有引用。

4. 为主响应声明额外的 Media Types

同一个 responses 参数还可以用来为主响应200)声明不同的 media type。例如声明 path operation 可能返回 JSON object(media type application/json)或 PNG 图像:

from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel


class Item(BaseModel):
    id: str
    value: str


app = FastAPI()


@app.get(
    "/items/{item_id}",
    response_model=Item,
    responses={
        200: {
            "content": {"image/png": {}},
            "description": "Return the JSON item or an image.",
        }
    },
)
async def read_item(item_id: str, img: bool | None = None):
    if img:
        return FileResponse("image.png", media_type="image/png")
    else:
        return {"id": "foo", "value": "there goes my hero"}

(完整示例见 tutorial002_py310.py。)

两个来自官方文档的注意事项:

  1. 图像必须通过 FileResponse 直接返回,而不能返回一个 Image 对象之类的东西再指望 FastAPI 帮你序列化——200 声明里的 application/json 分支由 response_model 自动覆盖,而 image/png 分支只是 Schema 层面的声明,实际字节流由你返回的 FileResponse 提供;
  2. 关于 media type 的默认规则:只要你没有在 responses 参数中显式指定其他 media type,FastAPI 会假定该响应的 media type 与主响应类相同(默认即 application/json)。但如果你指定了一个 media type 为 None 的自定义响应类,FastAPI 会对带 model 的额外 response 回退使用 application/json——这与 openapi/utils.py 中的取值逻辑 完全一致:media_type = route_response_media_type or "application/json"

5. 合并来自多处的 Response 信息

响应信息可以来自多个来源并自动合并response_modelstatus_coderesponses 参数。例如用默认的 200(或自定义 status code)声明 response_model,同时通过 responses同一个 200 响应补充 OpenAPI 层面的额外信息;对 404 则同时使用 Pydantic model 和自定义 description

FastAPI 会保留 responses 中的额外信息,并将其与 model 生成的 JSON Schema 合并。示例(完整代码见 tutorial003_py310.py):

@app.get(
    "/items/{item_id}",
    response_model=Item,
    responses={
        404: {"model": Message, "description": "The item was not found"},
        200: {
            "description": "Item requested by ID",
            "content": {
                "application/json": {
                    "example": {"id": "bar", "value": "The bar tenders"}
                }
            },
        },
    },
)
async def read_item(item_id: str):
    if item_id == "foo":
        return {"id": "foo", "value": "there goes my hero"}
    else:
        return JSONResponse(status_code=404, content={"message": "Item not found"})

合并后的 200 响应最终同时拥有:response_model=Item 带来的 schema: {"$ref": "#/components/schemas/Item"}responses 中提供的 description,以及 content 中的 example。这正是 deep_dict_update 深合并的结果。

该行为有测试用例背书:test_tutorial003.py 使用 inline_snapshot 精确断言了 /openapi.json 的完整结构——404 携带 Message$ref 与描述 "The item was not found"200 携带 Item$ref、描述 "Item requested by ID" 和示例 {"id": "bar", "value": "The bar tenders"},同时 422 自动注入。仓库中 test_tutorial001.pytest_tutorial004.py 分别覆盖了本节前面 4 个示例的运行时行为与 OpenAPI 输出。

官方文档中还附有一张交互式 API 文档的截图(见本文开头的配图),展示了上述合并信息在 /docs 页面中的实际呈现效果。

6. 复用预定义 Responses 与自定义 Responses 的组合

实际项目中,你往往希望维护一组通用的、可跨多个 path operation 复用的预定义 responses(如 404/403/302),再在单个接口上叠加自定义项。官方文档给出的方案是 Python 的 **dict_to_unpack 字典解包技巧:

old_dict = {
    "old key": "old value",
    "second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}

new_dict 会同时拥有 old_dict 的全部键值对和新加的键值对:

{
    "old key": "old value",
    "second old key": "second old value",
    "new key": "new value",
}

把这个技巧用到 path operations 上(完整示例见 tutorial004_py310.py):

from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel


class Item(BaseModel):
    id: str
    value: str


responses = {
    404: {"description": "Item not found"},
    302: {"description": "The item was moved"},
    403: {"description": "Not enough privileges"},
}


app = FastAPI()


@app.get(
    "/items/{item_id}",
    response_model=Item,
    responses={**responses, 200: {"content": {"image/png": {}}}},
)
async def read_item(item_id: str, img: bool | None = None):
    if img:
        return FileResponse("image.png", media_type="image/png")
    else:
        return {"id": "foo", "value": "there goes my hero"}

这里 404302403 三个通用响应被定义在模块级变量 responses 中,装饰器里通过 {**responses, 200: {...}} 一次性合并了预定义项与本接口的 image/png 附加声明。若某个键重复,后出现的键值对会覆盖先出现的——即单个接口的自定义项优先。

值得一提的是,这种"预定义 + 叠加"的模式在 FastAPI 中是框架级内置能力APIRouterinclude_router 本身就支持 responses 参数,并且按 Router 的 responses 在前、被 include 方的 responses 在后 的顺序做 **{**parent, **child} 合并(见 APIRouter 的合并逻辑include_router 的合并)。因此你既可以在 APIRouter(prefix=..., responses=common_responses) 上统一声明通用响应,也可以在 app.include_router(router, responses=...) 时再叠加一层,最后由各 path operationresponses 做最细粒度的覆盖——与本文示例的 **dict 解包技巧互为表里。

7. responses 里到底能写什么

responses 中每个状态码对应的 dict,可以包含 OpenAPI 规范 Responses Object / Response Object 章节中定义的几乎任何字段。OpenAPI 3.1 规范的 Response Object 主要字段包括:

  • description:响应的描述(缺省时 FastAPI 按第 2.2 节的回退链自动补齐);
  • headers:该响应可能携带的额外响应头;
  • content:核心字段,一个按 media type(如 application/jsonimage/png)划分的 dict,每个 media type 下可声明 schema(JSON Schema)、exampleexamples 等;
  • links:指向基于该响应结果的后续操作的链接定义。

也就是说,除了 FastAPI 扩展的 model 键之外,你可以在每个 response 的 dict 里直接写上述规范的任意内容。同时请注意 responses 的 key 除了具体状态码(404302 等)外,还支持 "default"(源码中同时兼容整数写法,"DEFAULT" 会被规范化为 "default"),用于描述"其他所有状态码"的通用响应。

8. 实践清单与注意事项

结合官方文档与仓库源码,使用 responses 参数时的要点:

  1. 声明与实际返回必须一致:为每个声明的额外状态码,在 endpoint 中显式返回 JSONResponseFileResponseResponse 实例,带上正确的 status code 和 content;
  2. model 键仅用于生成 Schema:它不是 OpenAPI 字段,且仅适用于允许携带响应体的状态码(无 body 的状态码会触发断言错误);
  3. media type 默认规则:不显式声明时与主响应类一致(默认 application/json);自定义响应类 media type 为 None 时,带 model 的额外响应回退到 application/json
  4. 合并语义是深合并response_modelstatus_coderesponses 三处的信息会由 FastAPI 深合并(deep_dict_update),同一路径操作内的 responses 项优先补充/覆盖;
  5. 复用模式:模块级预定义 dict + ** 解包,或 APIRouter/include_routerresponses 参数,均可实现"公共响应 + 个性响应"的分层组织;
  6. 验证手段:用 TestClient 请求 /openapi.json 并用 inline_snapshot 之类工具对完整结构做快照断言,仓库中的 tests/test_tutorial/test_additional_responses/ 提供了 4 组可直接参考的测试写法。

9. 参考文件

内容 路径
原始文档(印地语版,本文主体依据) additional-responses.md
示例 1:model 键声明 404 响应 tutorial001_py310.py
示例 2:为主响应声明 image/png media type tutorial002_py310.py
示例 3:response_modelresponses 信息合并 tutorial003_py310.py
示例 4:**dict 解包复用预定义 responses tutorial004_py310.py
路由注册与 responses 校验、model 字段创建 fastapi/routing.py
OpenAPI 生成:responses 深合并、model 剥离、422 注入 fastapi/openapi/utils.py
对应测试(OpenAPI 快照断言) tests/test_tutorial/test_additional_responses/
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388