首页
/ FastAPI 扩展 OpenAPI 指南:自定义 `/openapi.json` 生成过程与完整实战

FastAPI 扩展 OpenAPI 指南:自定义 `/openapi.json` 生成过程与完整实战

2026-09-08 21:26:26作者:董灵辛Dennis

FastAPI 会自动为应用生成符合 OpenAPI 规范的 API 文档,而当你需要为文档注入自定义 Logo、修改标题描述、添加供应商扩展(Vendor Extensions)时,就必须介入默认的 OpenAPI 生成流程。本文基于仓库中的官方教程(葡萄牙语文档 docs/pt/docs/how-to/extending-openapi.md,英文原文见 docs/en/docs/how-to/extending-openapi.md)展开,结合 fastapi/openapi/utils.pyfastapi/applications.py 的源码实现,讲透 OpenAPI 的默认生成机制、get_openapi() 全部核心参数,并给出一个可复制、可运行的“自定义 Logo”完整示例,让你能随意改写 API 文档的任意部分。

默认的 OpenAPI 生成流程(The Normal Process)

在动手覆盖之前,必须先理解 FastAPI 默认是如何生成 OpenAPI 架构(schema)的。整个流程分为三步:

  1. FastAPI 应用实例拥有一个 .openapi() 方法,它负责返回 OpenAPI 架构(一个 Python 字典)。
  2. 在创建应用对象时,会自动注册一条指向 /openapi.json路径操作路由(路径取决于 openapi_url 参数,默认就是 /openapi.json)。这条路由本身不做任何额外处理,只是把 .openapi() 方法的返回值以 JSON 响应返回。
  3. 默认情况下,.openapi() 方法会先检查 .openapi_schema 属性是否有内容:如果有,直接返回;如果没有,就调用工具函数 fastapi.openapi.utils.get_openapi 生成,并把结果缓存到 .openapi_schema 中。

换句话说,用户每次打开 Swagger UI(/docs)或 ReDoc(/redoc),页面都会请求 /openapi.json,最终由 .openapi() 方法给出 JSON 数据。

get_openapi() 的核心参数

get_openapi() 是生成 OpenAPI 架构的“发动机”,它的关键参数如下:

参数 含义 默认值 / 说明
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 用它来收集所有已注册的路径操作,包括被 include_router 引入的子路由 必填

技术细节(Tip)app.routes 是一个底层路由树,其中可能包含 FastAPI 为被引入的 Router 内部使用的“候选路由”,而不仅仅是最终的 APIRoute 对象。不过你仍然可以把 app.routes 直接传给 get_openapi()——FastAPI 会遍历这棵路由树,提取出真正生效的路径操作。该逻辑在 fastapi/openapi/utils.pyget_fields_from_routes()get_openapi() 主函数中通过 routing.iter_route_contexts() 实现。

注意(Note)summary 参数从 OpenAPI 3.1.0 规范开始可用,对应 FastAPI 0.99.0 及以上版本才支持。

覆盖默认流程:自定义 OpenAPI 架构(Overriding the Defaults)

掌握了上述流程后,思路就非常清晰了:复用 get_openapi() 这个工具函数生成架构,再对返回的字典做任意修改,最后把应用默认的 .openapi() 方法替换成自己的函数

下面以“为 ReDoc 添加自定义 Logo(x-logo 供应商扩展)”为例,演示完整过程。完整源码见 docs_src/extending_openapi/tutorial001_py310.py

第一步:先写一个正常的 FastAPI 应用

照常编写应用,定义路由即可:

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

app = FastAPI()


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

第二步:用同一个工具函数生成 OpenAPI 架构

在自定义函数 custom_openapi() 内部调用 get_openapi(),并传入自定义的 titleversionsummarydescription 以及 app.routes

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,
    )
    # ... 后续修改与缓存步骤

注意这里复用的正是默认流程使用的 fastapi.openapi.utils.get_openapi,所以生成的架构与默认版本完全一致,只是应用了你自定义的元信息。

第三步:修改 OpenAPI 架构

现在可以对返回的字典做任意修改了。OpenAPI 架构的顶层包含 infopathscomponents 等键,其中 info 是一个“对象”。我们往 info 里添加 ReDoc 支持的 x-logo 供应商扩展:

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

x-logo 是 ReDoc 的官方供应商扩展(Vendor Extension),OpenAPI 规范允许以 x- 开头的自定义字段存在于规范允许的位置,ReDoc 会读取 info 下的 x-logo 并在页面右上角渲染该图片。

第四步:把生成的架构缓存起来

每次用户打开 API 文档都重新生成一次完整架构是浪费的。FastAPI 默认就使用 app.openapi_schema 属性作为缓存:首次生成后存入,后续请求直接复用同一个缓存对象。我们同样利用这个机制:

    app.openapi_schema = openapi_schema
    return app.openapi_schema

custom_openapi() 的开头(if app.openapi_schema: return app.openapi_schema)加上缓存检查,这样架构只生成一次,之后的请求全部命中缓存。

第五步:覆盖应用的 .openapi() 方法

把应用默认的 .openapi() 方法替换为我们的自定义函数:

app.openapi = custom_openapi

这一步是关键:因为 /openapi.json 路由在响应时调用的是 app.openapi(),替换之后,Swagger UI、ReDoc 以及任何直接请求 /openapi.json 的客户端拿到的都是我们定制后的架构。

完整的 custom_openapi() 函数如下(便于直接复制运行):

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


app.openapi = custom_openapi

第六步:验证效果

启动应用(uvicorn 运行入口文件后),访问 http://127.0.0.1:8000/redoc,即可看到 ReDoc 页面右上角显示的是自定义 Logo(本例中为 FastAPI 的 Logo):

ReDoc 页面使用自定义 x-logo 的效果

源码级原理:默认 .openapi() 到底做了什么

为了让覆盖过程更有把握,我们直接看 fastapi/applications.pyFastAPI.openapi() 的实现。它的核心逻辑是:

  1. 先通过 self.router._get_routes_version() 获取当前路由版本号;
  2. 如果 .openapi_schema 为空,或路由版本发生了变化self._openapi_routes_version != routes_version),就调用 get_openapi() 重新生成;
  3. 生成时把 titleversionopenapi_versionsummarydescriptionterms_of_servicecontactlicense_inforouteswebhookstagsserversseparate_input_output_schemasexternal_docs 等应用属性全部透传给 get_openapi()
  4. 记录路由版本号,返回缓存的 self.openapi_schema

值得注意的是第 2 点:FastAPI 内置的缓存并不是“一劳永逸”的,当检测到路由有变化时会自动重新生成架构。而文档示例中的自定义 custom_openapi() 只检查 app.openapi_schema 是否有内容,属于“永久缓存”策略——如果你的应用会在运行期动态添加路由,可以考虑在自定义实现里同样引入版本号检查。

再看 /openapi.json 路由的注册,位于 fastapi/applications.pysetup() 方法:如果设置了 openapi_url,FastAPI 会注册一个内部 openapi 协程路由,它从请求中读取 root_path,调用 self.openapi() 获取架构,在需要时把 root_path 注入 servers,最后用 JSONResponse 返回。这也解释了为什么替换 app.openapi 就能全局生效——该路由响应时调用的正是这个属性。

get_openapi() 的生成过程

fastapi/openapi/utils.py 中,get_openapi() 的生成流程大致为:

  • 组装顶层 info 对象(titleversionsummarydescriptiontermsOfServicecontactlicense);
  • 初始化 openapi(版本号)、serverspathswebhookscomponents 等顶层结构;
  • 收集所有路由涉及的请求体字段、响应字段、路径/查询/请求头/Cookie 参数,统一扁平化为模型(flat models)并生成 definitions(即 components/schemas);
  • 逐个遍历路由树中的 APIRoute,通过 get_openapi_path() 生成每个路径下的 operation(方法、标签、参数、请求体、响应、回调、校验错误 422 响应等);
  • 处理 Webhooks 路由、tagsexternalDocs
  • 最终通过 OpenAPI(**output) 模型 + jsonable_encoder 序列化为字典输出。

理解这一点后,你会发现“扩展 OpenAPI”其实就是三步套路:生成 → 修改字典 → 缓存并替换方法。无论你想改 infopathscomponents 中的任何内容,还是往任意层级塞 x- 供应商扩展,都适用这一套路。

测试验证:仓库如何保证自定义逻辑正确

仓库为这个教程示例提供了完整的自动化测试,见 tests/test_tutorial/test_extending_openapi/test_tutorial001.py。该测试使用 TestClient 断言:

  • GET /items/ 返回 [{"name": "Foo"}],证明业务路由不受影响;
  • GET /openapi.json 返回的架构中,info.title"Custom title"info.summary 为自定义摘要、info.version"2.5.0",且 info 下确实包含了 x-logo 字段;
  • 连续请求两次 /openapi.json 得到完全一致的结果,验证了自定义缓存逻辑(架构只生成一次)的有效性。

如果你在自己项目中实现了类似的扩展,可以参考这个测试模式:既校验业务接口不受影响,又精确校验 OpenAPI 输出结构,同时验证缓存行为。

更进一步:还能扩展什么

掌握了“生成 → 修改 → 缓存 → 替换”的完整套路后,你还可以轻松实现这些常见定制:

  • 修改 API 文档的品牌信息:替换 info 中的 titledescriptioncontactlicense,甚至加入 x-logo
  • 给整个文档补充 tagsexternalDocsget_openapi() 支持这些参数,但默认的 app.openapi 只透传应用创建时传入的属性,自定义函数中可以额外补充;
  • 按需过滤或改写 paths:比如剔除内部接口、给路径加统一前缀、改写 operationId 等;
  • 注入自定义供应商扩展:如 x-code-samples(代码示例)、x-logo 等 ReDoc/Swagger UI 支持的扩展字段。

值得注意的是,FastAPI 也提供了更轻量的声明式方案:在路径操作层面用 openapi_extra 直接合并额外字段(见 fastapi/openapi/utils.pydeep_dict_update(operation, route.openapi_extra))。当你的定制只影响单个接口时,openapi_extra 往往比全局覆盖更简洁;而需要全局统一改动时,本文的 .openapi() 覆盖方案则是最佳选择。

总结

  • 默认情况下,/openapi.json 路由调用 app.openapi(),其内部使用 fastapi.openapi.utils.get_openapi 生成架构,并用 app.openapi_schema 做缓存。
  • 自定义扩展的标准做法:在 custom_openapi() 中先检查缓存,再调用 get_openapi(title=..., version=..., summary=..., description=..., routes=app.routes) 生成字典,直接修改字典内容,写回 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