首页
/ FastAPI 扩展 OpenAPI Schema:用 get_openapi 自定义文档与厂商扩展

FastAPI 扩展 OpenAPI Schema:用 get_openapi 自定义文档与厂商扩展

2026-09-07 22:39:02作者:胡易黎Nicole

FastAPI 会根据路由自动生成 OpenAPI Schema(即 /openapi.json 返回的内容),并把它作为 Swagger UI 与 ReDoc 文档的数据源。本文以仓库文档 docs/fr/docs/how-to/extending-openapi.md 为主线,结合 FastAPI 源码讲解 Schema 的默认生成流程,并给出一个完整的"自定义 OpenAPI"实战方案:通过覆盖 app.openapi 并复用 fastapi.openapi.utils.get_openapi,在保留全部自动生成结果的基础上,为文档注入自定义的 ReDoc 厂商扩展(如自定义 Logo)。读完你将能独立实现任意 OpenAPI 字段的增改,且不破坏原有的缓存与性能特性。

默认的 OpenAPI 生成流程

.openapi() 方法与 /openapi.json 端点

每个 FastAPI 应用实例都拥有一个 .openapi() 方法,其职责是返回完整的 OpenAPI Schema 字典。在应用对象创建时,FastAPI 会注册一个指向 /openapi.json(或你在 FastAPI(openapi_url=...) 中指定的任意 URL)的路径操作,它仅仅把 .openapi() 的结果包装成 JSON 响应返回。

在源码 fastapi/applications.pysetup() 中可以确认这条链路:

if self.openapi_url:
    async def openapi(req: Request) -> JSONResponse:
        root_path = req.scope.get("root_path", "").rstrip("/")
        schema = self.openapi()
        ...
        return JSONResponse(schema)
    self.add_route(self.openapi_url, openapi, include_in_schema=False)

注意两点:

  • 端点路由是在运行时通过 self.openapi() 属性动态取用的,因此稍后把 app.openapi 替换成自定义函数,这一注册行为不受影响;
  • 只有当 openapi_url 非空时才会注册(例如 FastAPI(openapi_url=None) 可以整体禁用该端点)。

.openapi_schema 缓存与默认实现

默认情况下,.openapi() 方法的第一步是检查实例属性 .openapi_schema:如果已有内容就直接返回;否则调用工具函数 fastapi.openapi.utils.get_openapi 现场生成。生成结果会写回 self.openapi_schema,实现"只生成一次、后续直接读取缓存"的效果。self.openapi_schemafastapi/applications.py 中被初始化为 None

源码中的默认实现(见 fastapi/applications.py)实际上还加入了一层路由变更失效机制:

def openapi(self) -> dict[str, Any]:
    routes_version = self.router._get_routes_version()
    if not self.openapi_schema or self._openapi_routes_version != routes_version:
        self.openapi_schema = get_openapi(
            title=self.title,
            version=self.version,
            openapi_version=self.openapi_version,
            summary=self.summary,
            description=self.description,
            terms_of_service=self.terms_of_service,
            contact=self.contact,
            license_info=self.license_info,
            routes=self.routes,
            webhooks=self.webhooks.routes,
            tags=self.openapi_tags,
            servers=self.servers,
            separate_input_output_schemas=self.separate_input_output_schemas,
            external_docs=self.openapi_external_docs,
        )
        self._openapi_routes_version = routes_version
    return self.openapi_schema

也就是说:内置实现除了缓存之外,还会在路由树版本变化(例如应用启动后新增了路由)时自动重新生成 Schema。

get_openapi() 的参数

工具函数 get_openapi() 定义于 fastapi/openapi/utils.py。官方文档列出了它的核心参数,实际签名还包含更多可选参数,统一整理如下:

参数 含义 默认值
title OpenAPI 标题,会显示在文档页 必填
version API 版本号,例如 2.5.0 必填
openapi_version 使用的 OpenAPI 规范版本 3.1.0(最新版)
summary API 的简短摘要 None
description API 详细描述,支持 Markdown,会渲染在文档页 None
terms_of_service 服务条款 URL None
contact 联系信息对象(name/url/email None
license_info 许可证信息对象(name/identifier 等) None
routes 应用路由(取自 app.routes 必填
webhooks Webhook 路由列表 None
tags OpenAPI 顶层标签描述列表 None
servers OpenAPI servers 数组 None
separate_input_output_schemas 是否为输入/输出分别生成独立 Schema True
external_docs externalDocs 对象 None

技术细节app.routes 是一个更底层的路由树,其中可能包含 FastAPI 为被 include 的路由器内部使用的候选路由,而不仅仅是最终的 APIRoute 对象。尽管如此,你仍可以直接把 app.routes 传给 get_openapi(),FastAPI 会遍历这棵路由树,收集其中真实生效的路径操作(参见 fastapi/openapi/utils.py 中对 routes 展开与字段收集的实现)。

版本提示summary 参数属于 OpenAPI 3.1.0 及以上规范的能力,FastAPI 0.99.0 及以上版本才支持该参数;低于此版本时应忽略它。

覆盖默认值:给 ReDoc 注入自定义 Logo

理解了默认流程后,就能采用"复用同一工具函数生成 Schema,再逐项覆盖需要修改的部分"这一通用套路。下面以最典型的场景——为 ReDoc 添加自定义 Logo(x-logo 厂商扩展,vendor extension)为例,完整演示。

第 1 步:编写常规 FastAPI 应用

先按平常方式写一个应用,本示例存放于 docs_src/extending_openapi/tutorial001_py310.py

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。这里可以传入你想要的 titleversionsummarydescription,并传入 routes=app.routes

def custom_openapi():
    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,
    )
    ...

借助这一步,你可以"免费"获得原本自动生成的全部内容:pathscomponents/schemassecuritySchemes、422 校验错误响应定义等,因为它们统统由 get_openapi() 依据路由推导而来。

第 3 步:修改生成的 Schema

拿到 Schema 字典后,就可以像操作普通 dict 一样加入自定义内容。ReDoc 的 x-logo 扩展放在 Schema 顶层的 info 对象里,指向一张 Logo 图片 URL:

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

x- 开头的字段是 OpenAPI 规范的"厂商扩展"(Vendor Extensions)机制,工具链遇到不认识但以 x- 开头的键都会保留并透传,因此这是在不破坏规范的前提下注入自定义元数据的标准做法。

第 4 步:把 Schema 存入缓存

把修改后的 Schema 写回 app.openapi_schema 属性作为"缓存"。这样应用不必在每次用户打开 API 文档时都重新生成一次 Schema——它只被生成一次,后续所有请求都直接复用这份缓存结果:

    app.openapi_schema = openapi_schema
    return app.openapi_schema

第 5 步:覆盖 .openapi() 方法

最后把应用默认的 .openapi() 方法替换为你的新函数:

app.openapi = custom_openapi

由于第 4 步已经把结果写回缓存,第 5 步覆盖之后,无论是 /openapi.json 还是文档页内部的 Schema 加载,都会命中 custom_openapi() 的逻辑:首次调用生成并缓存,此后直接返回缓存。缓存命中分支同样应在函数开头显式处理(下节给出完整代码)。

完整代码与效果验证

把上述五步串起来(含缓存判空逻辑),得到完整实现如下:

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

app = FastAPI()


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


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

运行应用后访问 http://127.0.0.1:8000/redoc,可以看到文档页顶部使用了自定义 Logo(本例为 FastAPI 自身的 Logo),同时右侧仍完整展示 /items/GET 路径操作及其响应模型,说明自定义 Schema 并没有影响自动推导的路由信息:

在 ReDoc 文档页顶部展示自定义 x-logo 扩展的效果

同样的改动也作用于 http://127.0.0.1:8000/docs(Swagger UI)以及直接访问 http://127.0.0.1:8000/openapi.json 返回的原始 JSON——三者共享同一份 app.openapi() 结果。你也可以在浏览器中直接查看 /openapi.json,确认 info 对象中已多出 x-logo 字段,以及 titlesummarydescription 均被替换为自定义值。

注意事项与更深入的自定义方向

  • 性能与缓存语义:内置 .openapi() 的缓存还带有"路由版本变化即失效重算"的逻辑;而本教程的自定义函数中,缓存命中后直接返回,Schema 是静态固定的。如果你在应用运行期动态追加路由且希望其进入 Schema,需要自行设计失效策略(例如监听路由变化后清空 app.openapi_schema)。
  • summary 的可用前提:只有 FastAPI 0.99.0+(对应 OpenAPI 3.1.0)才支持 summary 参数,老旧工具链可能不识别该字段。
  • 其他相关主题:若需要"仅在特定环境暴露 /docs/openapi.json",可参考 conditional-openapi.md;若想定制 Swagger UI 本身的参数(如默认展开深度),见 configure-swagger-ui.md;文档页面 JS/CSS 等静态资源替换则见 custom-docs-ui-assets.md

这套"复用 get_openapi() + 覆盖 app.openapi + 用 app.openapi_schema 做缓存"的三段式写法,是扩展 FastAPI 生成式 Schema 的标准范式。基于它,你不仅可以添加 x-logo,还能按需改写 infotagsservers,甚至逐路径调整 pathscomponents,实现完全贴合业务的 API 文档输出。

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

项目优选

收起
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
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
521
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
392