首页
/ FastAPI 自定义扩展 OpenAPI schema:用 get_openapi 覆盖默认生成逻辑

FastAPI 自定义扩展 OpenAPI schema:用 get_openapi 覆盖默认生成逻辑

2026-09-07 23:16:07作者:魏侃纯Zoe

导读:FastAPI 会根据应用的 path operation 自动生成一份 OpenAPI 3.1.0 schema,并暴露为 /openapi.json,供 Swagger UI、ReDoc 等交互式文档消费。本文以在文档页中注入自定义 Logo(ReDoc 的 x-logo 扩展)为主线,讲解 OpenAPI schema 的默认生成流程、get_openapi() 核心参数,以及通过覆写 app.openapi 方法实现对生成结果的深度定制;读完后你将掌握一套可复用的 schema 定制模板,并能看懂其背后的缓存与路由采集原理。文中示例源码位于 docs_src/extending_openapi/tutorial001_py310.py,官方教程正文在 docs/es/docs/how-to/extending-openapi.md

默认情况下 schema 是如何生成的

要对生成结果做定制,首先必须理解默认的"正常流程"。整个过程由 FastAPI 应用实例上的三个角色配合完成,在 fastapi/applications.py 中都能找到对应实现:

  1. .openapi() 方法:返回整个 OpenAPI schema(一个 dict)。
  2. .openapi_schema 属性:schema 的内存级缓存。默认 .openapi() 会先检查该属性是否已有内容,有则直接返回,避免每次请求都重新生成。
  3. fastapi.openapi.utils.get_openapi 工具函数:当缓存为空时,真正负责"从零生成" schema。

同时在应用对象创建阶段,FastAPI 会注册一条指向 /openapi.json(或你在 openapi_url 中配置的任意路径)的 path operation。这条路由只是把 .openapi() 的返回值包装成 JSON 响应返回。在 setup() 方法中可以看到这段逻辑:self.add_route(self.openapi_url, openapi, include_in_schema=False),其中 openapi 是一个异步端点,内部调用 self.openapi() 并返回 JSONResponse;若配置了 root_pathroot_path_in_servers=True,还会在 servers 顶部注入服务地址。

.openapi() 的默认实现(fastapi/applications.py)比文档描述的"查缓存"多了一层细节:它会用 self.router._get_routes_version() 记录路由版本号,当路由树发生变化(例如新注册了路由)而版本号与缓存生成时不一致时,会自动失效缓存并重新生成。也就是说,默认实现本身已具备一定的缓存新鲜度保障——这一点在覆写方法时值得留意(见下文"缓存与路由变更")。

get_openapi() 的核心参数

当缓存未命中时,生成工作交给 get_openapi()。该函数位于 fastapi/openapi/utils.py,文档明确列出的参数有:

  • title:OpenAPI 的标题,会显示在文档页上。
  • version:你的 API 版本,例如 2.5.0
  • openapi_version:使用的 OpenAPI 规范版本,默认最新版 3.1.0
  • summary:API 的简短摘要。该参数在 OpenAPI 3.1.0 及以上才有,FastAPI 0.99.0 及以上版本支持。
  • description:API 描述,支持 Markdown,会渲染在文档页中。
  • routes:取自 app.routes 的应用路由。FastAPI 依据它收集所有已注册 path operation,包括通过 include_router() 挂载进来的路由。

技术细节:routes 到底是什么

从源码注释与函数签名(routes: Sequence[BaseRoute | routing.RouteContext])可以确认一个容易被误解的点:app.routes 是一个低层路由树,它内部不仅包含最终的 APIRoute 对象,还可能含有 FastAPI 处理"被 include 的 router"时使用的候选路由节点。不过你依然可以直接把 app.routes 传给 get_openapi()——FastAPI 内部通过 routing.iter_route_contexts() 遍历这棵树并筛选出"有效的 path operation"(见 get_openapi 实现 中对 _get_api_route_for_openapi 的过滤)。

签名中还有哪些可覆盖参数

对照 utils.py 的函数签名可以发现,除了文档列出的 6 个参数,get_openapi() 还接受 webhookstagsserversterms_of_servicecontactlicense_infoseparate_input_output_schemasexternal_docs 等可选参数。它们分别对应 OpenAPI 顶层对象中的 webhookstagsserversinfo.termsOfServiceinfo.contactinfo.license 等字段,并支持 info 中嵌入这些扩展信息。这意味着即使不逐字段手改 dict,也能通过参数实现相当大范围的定制。

覆写默认值:给 ReDoc 加一个自定义 Logo

文档给出的经典案例是:使用同一个工具函数 get_openapi() 生成 schema,再覆写需要修改的部分——这里以加入 ReDoc 的厂商扩展 x-logo(自定义 Logo)为例。整个实现分五步。

第 1 步:正常编写 FastAPI 应用

先像平常一样定义一个 FastAPI 实例和一个路径操作:

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"name": "Foo"}]

第 2 步:用工具函数生成 schema

新建一个 custom_openapi() 函数,在其内部调用 get_openapi() 生成 schema。这一步复用了 FastAPI 默认的生成能力,而不是手写整份 JSON:

def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema
    openapi_schema = get_openapi(
        title="Custom title",
        version="2.5.0",
        summary="This is a very custom OpenAPI schema",
        description="Here's a longer description of the custom **OpenAPI** schema",
        routes=app.routes,
    )
    openapi_schema["info"]["x-logo"] = {
        "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
    }
    app.openapi_schema = openapi_schema
    return app.openapi_schema

第 3 步:修改 schema 对象

因为 get_openapi() 返回的是普通 Python dict,生成后可以像操作任何字典一样直接修改。这里向 info 对象中追加 ReDoc 的 x-logo 扩展:

openapi_schema["info"]["x-logo"] = {
    "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
}

info 中的 titleversionsummarydescription 均由第二步传入的参数填充,x-logo 则是额外塞进去的 ReDoc 专属字段。理论上你可以在生成后对 schema 的任何顶层或嵌套对象(pathscomponents.schemas、单个 operation 等)做同样的字典级修改,这正是"extending OpenAPI"最通用的姿势。

第 4 步:缓存 schema

生成的 schema 比较昂贵(要遍历全部路由、汇总 Pydantic 模型定义等),所以把结果写回 app.openapi_schema 作为缓存。这样 schema 只生成一次,之后所有请求(例如用户反复打开文档页)都会直接命中缓存:

app.openapi_schema = openapi_schema
return app.openapi_schema

注意第 2 步开头的 if app.openapi_schema: 检查:第二次调用 custom_openapi() 时缓存已存在,直接返回,避免重复生成。

第 5 步:用新函数替换默认方法

最后,把实例方法替换为自定义函数。由于 /openapi.json 端点内部调用的是 self.openapi(),替换后该端点(以及 Swagger UI、ReDoc 拉取的 schema)就会自动走你的定制逻辑:

app.openapi = custom_openapi

验证效果与测试佐证

保存并运行应用后,访问 http://127.0.0.1:8000/redoc 即可看到文档导航栏顶部显示自定义 Logo(本例为 FastAPI 官方 Logo),标题与版本也会变成 Custom title (2.5.0)

FastAPI ReDoc 页面展示通过 x-logo 扩展注入的自定义 Logo 与自定义标题 Custom title (2.5.0)

这个例子并非孤证——仓库中有配套的自动化测试 tests/test_tutorial/test_extending_openapi/test_tutorial001.py。测试先请求 /items/ 断言业务接口正常,再请求 /openapi.json 并以内联快照(inline snapshot)逐字段校验生成结果,其中 info 对象精确包含:

{
  "title": "Custom title",
  "summary": "This is a very custom OpenAPI schema",
  "description": "Here's a longer description of the custom **OpenAPI** schema",
  "version": "2.5.0",
  "x-logo": {
    "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
  }
}

有趣的是测试还验证了文档强调的"缓存只生成一次":代码注释 # Request again to test the custom cache 之后再次请求 /openapi.json,并断言两次响应的 JSON 完全一致。同时通过快照可以发现 get_openapi() 还自动生成了 openapi: 3.1.0、operation 的 summary/operationId、默认 200 响应等常规内容——这说明定制是在"完整生成"之上做增量修改,而不是放弃 FastAPI 的默认能力。

进阶注意事项

缓存与路由变更

默认实现的 .openapi() 会记录 _openapi_routes_version,在路由树版本变化时自动重建 schema。但一旦覆写为 custom_openapi,这个失效机制是否仍生效取决于你的实现。如果应用是启动后固定路由,上述"只缓存一次"的模式没有问题;如果会在运行时动态增删路由(较少见),则需要自己在 custom_openapi() 中加入类似的版本比对或失效策略。

惰性生成与属性赋值时机

app.openapi_schema 初始为 None(见 applications.py)。自定义函数中"先判空、再生成、再赋值"的顺序保证了 schema 在首次访问时才生成(惰性),而不是模块导入时立即计算,避免拖慢启动过程。

更细粒度的替代方案

如果只是为单个路由补充 OpenAPI 字段,不必走到覆写 .openapi() 的层面:FastAPI 的路由声明支持 openapi_extraget_openapi_path() 在生成单条 operation 时会通过 deep_dict_update(operation, route.openapi_extra) 将其合并进结果(见 utils.py)。覆写 .openapi() 更适合需要全局性修改(如注入第三方厂商扩展、统一改写所有 operationId 或 tags)的场景。

小结

FastAPI 把"生成 OpenAPI schema"做成了一个可插拔的流程:.openapi() 负责入口与缓存,get_openapi() 负责真正的生成。要扩展它,只需复用 get_openapi(title=..., version=..., routes=app.routes) 生成一份完整 schema,以普通 dict 的方式修改需要定制的部分(如 ReDoc 的 x-logo),写回 app.openapi_schema 缓存,最后用 app.openapi = custom_openapi 替换默认方法即可。参考实现见 docs_src/extending_openapi/tutorial001_py310.py,完整测试见 tests/test_tutorial/test_extending_openapi/test_tutorial001.py

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

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