news 2026/9/9 12:55:42

FastAPI 文档 UI 静态资源自定义:切换 CDN 与完全离线自托管

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FastAPI 文档 UI 静态资源自定义:切换 CDN 与完全离线自托管

FastAPI 文档 UI 静态资源自定义:切换 CDN 与完全离线自托管

【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi

FastAPI 的交互式 API 文档由Swagger UI(默认位于/docs)与ReDoc(默认位于/redoc)渲染,二者都需要加载若干 JavaScript 与 CSS 文件,默认从公共 CDN 拉取。当你的部署环境无法访问默认 CDN,或需要在内网、离线环境中使用文档时,你可以利用 docs/ja/docs/how-to/custom-docs-ui-assets.md 介绍的两种方案——切换自定义 CDN本地静态文件自托管——来完全掌控这些资源。读完本文,你将掌握如何关闭 FastAPI 内置的文档路由、用内部 HTML 生成函数重建文档页面,以及如何在同一个 FastAPI 应用中挂载StaticFiles实现"断网也能看文档"。

一、背景:文档页面为什么要"加载资源"

Swagger UI 与 ReDoc 本质上是运行在浏览器里的前端单页应用,它们需要一个渲染"外壳"(一段 HTML),HTML 里再通过<script><link>引用一套 JS/CSS 来获得交互能力。

打开 fastapi/applications.py 可以看到 FastAPI 应用在构造时的相关默认值:

参数默认值说明
docs_url"/docs"Swagger UI 文档路由,设为None可关闭
redoc_url"/redoc"ReDoc 文档路由,设为None可关闭
openapi_url"/openapi.json"OpenAPI schema 路由,设为None会连带自动禁用文档
swagger_ui_oauth2_redirect_url"/docs/oauth2-redirect"Swagger UI 的 OAuth2 回调辅助页

而 JS/CSS 的具体来源,定义在 fastapi/openapi/docs.py 的函数默认参数中,例如:

  • swagger_js_url默认https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js
  • swagger_css_url默认https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css
  • redoc_js_url默认https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js

也就是说,只要替换这几个 URL,就能把文档资源"搬"到你指定的任何位置。

二、方案一:让文档加载自定义 CDN

适用场景:默认cdn.jsdelivr.net在你的网络环境下不可达(例如某些 URL 受限的地区),或你希望统一走公司内部 CDN 以提升加载速度。改用https://unpkg.com/是最常见的例子。

2.1 关闭自动文档路由

FastAPI 创建应用时会自动注册/docs/redoc路由(见 fastapi/applications.py 的setup()方法),并把这些路由的 HTML 生成逻辑连同默认 CDN URL 一起固定下来。因此第一步是把它们关掉,改成手动接管:

from fastapi import FastAPI from fastapi.openapi.docs import ( get_redoc_html, get_swagger_ui_html, get_swagger_ui_oauth2_redirect_html, ) app = FastAPI(docs_url=None, redoc_url=None)

注意:docs_url=Noneredoc_url=None分别关掉两个文档入口,openapi_url(即/openapi.json)仍然保留。从源码看,文档路由的注册条件是if self.openapi_url and self.docs_url/if self.openapi_url and self.redoc_url,所以只要你保留openapi_url,后续手写的文档页面依然能通过app.openapi_url拿到 schema 地址。

2.2 用内部 HTML 生成函数重建文档页

关闭自动文档后,需要自己注册/docs/redoc两个path operation。FastAPI 在fastapi.openapi.docs模块里暴露了三个可复用的内部函数(定义见 fastapi/openapi/docs.py):

函数作用
get_swagger_ui_html(...)返回加载 Swagger UI 的HTMLResponse
get_swagger_ui_oauth2_redirect_html()返回 Swagger UI 的 OAuth2 跳转辅助页HTMLResponse
get_redoc_html(...)返回加载 ReDoc 的HTMLResponse

get_swagger_ui_html的关键参数如下(Swagger UI 侧):

  • openapi_url:文档 HTML 去拉取 OpenAPI schema 的 URL,这里直接复用app.openapi_url
  • title:页面标题,通常用app.title拼接后缀;
  • oauth2_redirect_url:OAuth2 回调地址,默认值可直接用app.swagger_ui_oauth2_redirect_url
  • swagger_js_url:Swagger UI 的JavaScript文件 URL,即你替换成自定义 CDN 的那一项;
  • swagger_css_url:Swagger UI 的CSS文件 URL,同样换成自定义 CDN。

ReDoc 侧大同小异,唯一的资源参数是redoc_js_url。完整实现(对应 docs_src/custom_docs_ui/tutorial001_py310.py):

from fastapi import FastAPI from fastapi.openapi.docs import ( get_redoc_html, get_swagger_ui_html, get_swagger_ui_oauth2_redirect_html, ) app = FastAPI(docs_url=None, redoc_url=None) @app.get("/docs", include_in_schema=False) async def custom_swagger_ui_html(): return get_swagger_ui_html( openapi_url=app.openapi_url, title=app.title + " - Swagger UI", oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url, swagger_js_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js", swagger_css_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css", ) @app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False) async def swagger_ui_redirect(): return get_swagger_ui_oauth2_redirect_html() @app.get("/redoc", include_in_schema=False) async def redoc_html(): return get_redoc_html( openapi_url=app.openapi_url, title=app.title + " - ReDoc", redoc_js_url="https://unpkg.com/redoc@2/bundles/redoc.standalone.js", )

几点实现细节:

  • include_in_schema=False:文档路由本身不应出现在生成的 OpenAPI schema / API 文档列表里;
  • OAuth2 回调路由的路径直接取app.swagger_ui_oauth2_redirect_url(默认/docs/oauth2-redirect),因此只要使用docs_url=None时也会一并关闭、需要手工补回;
  • @app.get(app.swagger_ui_oauth2_redirect_url, ...)意味着注册路径来自 FastAPI 实例属性,而不是写死字符串,便于后续统一修改。

2.3 为什么要保留 OAuth2 跳转页

get_swagger_ui_html生成的 HTML 里,当传入oauth2_redirect_url时会拼接出oauth2RedirectUrl: window.location.origin + '...'配置。当你的 API 对接了 OAuth2 提供方后,文档页上的Authorize授权流程需要先把浏览器带到认证服务器,拿到凭据后再跳回文档页继续交互——Swagger UI 在后台完成这一过程,而"跳回"这一步依赖的就是/docs/oauth2-redirect这个辅助页。因此,只要 Swagger UI 需要支持 OAuth2 授权,就必须保留这个路由。

2.4 加一个普通接口用于验证

为了确认改动没有破坏应用本身,再注册一个普通的path operation

@app.get("/users/{username}") async def read_user(username: str): return {"message": f"Hello {username}"}

2.5 验证效果

启动应用后访问 http://127.0.0.1:8000/docs,在开发者工具中观察 Network 面板,可以看到swagger-ui-bundle.js等资源是从unpkg.com加载的。仓库自带的测试也验证了这一行为,见 tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py:测试通过断言响应文本中包含https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.jshttps://unpkg.com/swagger-ui-dist@5/swagger-ui.css等 URL 来确认自定义 CDN 已生效,同时验证/docs/oauth2-redirect返回的是 Swagger UI 的 OAuth2 跳转脚本。

三、方案二:静态文件完全本地自托管(离线可用)

适用场景:应用运行在无法访问互联网的离线环境或隔离内网,连 CDN 也够不到,却仍需要完整的交互式 API 文档。

思路很简单:把文档需要的 JS/CSS 文件下载到项目本地,用 FastAPI 自己的StaticFiles把它当成普通静态资源对外提供,再让文档 HTML 的swagger_js_url等参数指向本站地址。对应示例见 docs_src/custom_docs_ui/tutorial002_py310.py。

3.1 项目文件结构

假设项目现在长这样:

. ├── app │ ├── __init__.py │ ├── main.py

在项目根目录新建一个存放静态文件的static/目录:

. ├── app │ ├── __init__.py │ ├── main.py └── static/

3.2 下载所需文件

在你的开发机(有网环境)上,把下列文件下载到static/目录——可直接用浏览器右键"链接另存为",文件名必须保持与下表一致:

用途文件获取地址
Swagger UIswagger-ui-bundle.jshttps://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js
Swagger UIswagger-ui.csshttps://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css
ReDocredoc.standalone.jshttps://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js

完成后目录应为:

. ├── app │ ├── __init__.py │ ├── main.py └── static ├── redoc.standalone.js ├── swagger-ui-bundle.js └── swagger-ui.css

说明:这三个文件即上文中get_swagger_ui_html/get_redoc_html默认参数的本地版本,下载版本号对齐源码中的默认值(swagger-ui-dist@5redoc@2)即可保证功能一致。

3.3 用 StaticFiles 挂载静态目录

main.py中导入并挂载StaticFiles

from fastapi.staticfiles import StaticFiles app = FastAPI(docs_url=None, redoc_url=None) app.mount("/static", StaticFiles(directory="static"), name="static")

app.mount("/static", ...)会把应用内static目录下的所有文件映射到 URL 前缀/static/下,name="static"用于在模板或反向解析中引用这个挂载点。

3.4 先验证静态文件能否访问

启动应用,访问 http://127.0.0.1:8000/static/redoc.standalone.js。浏览器应返回一段很长的压缩 JavaScript,其开头类似:

/*! For license information please see redoc.standalone.js.LICENSE.txt */ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")): ...

能看到这段内容,说明两件事:应用能正确提供静态文件、文件也已放在正确位置。

3.5 关闭自动文档,把 URL 指向本站

同样先把自动文档关掉(docs_url=None, redoc_url=None),然后像方案一那样重建文档路由,区别在于这次资源 URL 写的是本站路径

from fastapi import FastAPI from fastapi.openapi.docs import ( get_redoc_html, get_swagger_ui_html, get_swagger_ui_oauth2_redirect_html, ) from fastapi.staticfiles import StaticFiles app = FastAPI(docs_url=None, redoc_url=None) app.mount("/static", StaticFiles(directory="static"), name="static") @app.get("/docs", include_in_schema=False) async def custom_swagger_ui_html(): return get_swagger_ui_html( openapi_url=app.openapi_url, title=app.title + " - Swagger UI", oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url, swagger_js_url="/static/swagger-ui-bundle.js", swagger_css_url="/static/swagger-ui.css", ) @app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False) async def swagger_ui_redirect(): return get_swagger_ui_oauth2_redirect_html() @app.get("/redoc", include_in_schema=False) async def redoc_html(): return get_redoc_html( openapi_url=app.openapi_url, title=app.title + " - ReDoc", redoc_js_url="/static/redoc.standalone.js", ) @app.get("/users/{username}") async def read_user(username: str): return {"message": f"Hello {username}"}

仓库测试 tests/test_tutorial/test_custom_docs_ui/test_tutorial002.py 同样验证了这一行为:它断言/docs响应中包含/static/swagger-ui-bundle.js/static/swagger-ui.css/redoc响应中包含/static/redoc.standalone.js,并验证/users/john业务接口照常工作。

3.6 离线验证

关闭本机网络(如断开 WiFi),再次访问 http://127.0.0.1:8000/docs 并刷新页面——只要浏览器没把资源缓存掉、也确实断网,页面仍能正常渲染出可交互的 API 文档。至此,文档的 JS/CSS 已完全不依赖外部网络。

3.7 追求"零外网请求"时的补充细节

如果对完全离线要求严格,从 fastapi/openapi/docs.py 的默认参数可以看出还有两处"漏网"的外部引用值得一并处理:

  • get_redoc_htmlwith_google_fonts参数默认是True,生成的 ReDoc HTML 会额外引入https://fonts.googleapis.com/...的字体样式表。完全离线时,可在调用处显式传with_google_fonts=False关闭,或接受"文档可用、字体回退为默认"的效果;
  • get_swagger_ui_htmlswagger_favicon_url默认指向 FastAPI 官网的 favicon 图片,get_redoc_htmlredoc_favicon_url同理。介意的话可把 favicon 一并放进static/并传本地路径,实现真正全站零外链。

这些选项在你不干预时都不影响本教程的离线渲染结果,但它们决定了是否仍存在个别"发往公网"的请求。

四、底层原理:路由注册与 HTML 生成

理解"为什么这样改能生效",能帮你应对更复杂的定制需求。

路由注册时机。FastAPI 的setup()方法(fastapi/applications.py)在初始化阶段按条件注册内置路由:

  • openapi_url存在,注册/openapi.json路由;
  • openapi_urldocs_url同时存在,调用get_swagger_ui_html(并带上init_oauthswagger_ui_parameters等配置)注册 Swagger UI,同时注册 OAuth2 跳转路由;
  • openapi_urlredoc_url同时存在,调用get_redoc_html注册 ReDoc。

因此把docs_urlredoc_url设为None等于取消了这些自动路由,而你自己写的装饰器路由随后接管同名路径——由于include_in_schema=False,也不会污染 schema。

HTML 的生成方式get_swagger_ui_html(fastapi/openapi/docs.py)本质上是按模板拼接字符串,把openapi_url塞进url: '...'、把swagger_css_url/swagger_js_url塞进<link><script src>、把oauth2_redirect_url拼成oauth2RedirectUrl,最终返回HTMLResponseget_redoc_html则生成带<redoc spec-url="...">标签与redoc.standalone.js引用的页面。这也是为什么"换一个 URL 参数"就能让整个文档资源源切换——它们只是生成 HTML 时的字符串插值。

可进一步定制的参数。在源码中,get_swagger_ui_html还支持swagger_ui_parameters(默认合并dom_idlayoutdeepLinking等一组swagger_ui_default_parameters)与init_oauth;这些与本文主题相关的扩展能力在 fastapi/openapi/docs.py 中都有完整定义,如需调整 Swagger UI 行为(例如默认展开状态、OAuth 初始化选项)可直接查阅。

五、运行与回归验证

本地运行两个示例(示例代码位于 docs_src/custom_docs_ui/ 目录,分别对应两个tutorial*_py310.py):

fastapi dev docs_src/custom_docs_ui/tutorial001_py310.py

或使用 uvicorn:

uvicorn docs_src.custom_docs_ui.tutorial002_py310:app --reload

若想跑仓库自带的回归测试,可执行:

pytest tests/test_tutorial/test_custom_docs_ui/

两个测试文件分别覆盖了"自定义 CDN"与"本地静态文件"两条链路,是验证改动正确性的快捷参考。

小结

  • 想换文档资源源:docs_url=None, redoc_url=None关闭自动文档 → 用get_swagger_ui_html/get_redoc_html重建路由 → 把swagger_js_urlswagger_css_urlredoc_js_url指向目标 CDN;
  • 想彻底离线:把上述三个文件下载进static/app.mount("/static", StaticFiles(directory="static"), name="static")→ 文档路由的 URL 参数改成/static/...
  • 别忘了保留/docs/oauth2-redirect路由以支持 Swagger UI 的 OAuth2 授权流程;
  • 追求"零外网请求"时,可顺带处理with_google_fonts与 favicon 默认指向的外部资源。

两种方案的完整可运行示例与配套测试都在本仓库内(docs_src/custom_docs_ui/ 与 tests/test_tutorial/test_custom_docs_ui/),可以直接对照源码做更深度的定制。

【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 12:55:22

家用中型混动SUV怎么选?从空间到混动技术全解析

家用中型混动SUV这几年的热度&#xff0c;我估计不用多解释。从自主品牌到合资品牌&#xff0c;从新势力到传统大厂&#xff0c;几乎每一家都在这个细分市场里排兵布阵&#xff0c;新车一波接一波。我在这行做了十几年市场研究&#xff0c;经常被身边朋友问到同一个问题&#x…

作者头像 李华
网站建设 2026/9/9 12:55:17

Python租房数据分析毕设:爬虫、Django后端与Echarts可视化全链路实践

今年的毕业论文季又到了&#xff0c;去年有个学弟找我聊他的毕设选题&#xff0c;想做“租房数据分析”&#xff0c;但被导师反问了一句&#xff1a;你的系统到底在“分析”什么&#xff1f;如果只是把爬下来的数据用表格展示一遍&#xff0c;那不叫数据分析&#xff0c;叫搬运…

作者头像 李华
网站建设 2026/9/9 12:54:03

动态电压恢复器DVR的Simulink建模与仿真验证

电力行业的朋友对电压暂降应该都不陌生&#xff0c;生产线莫名其妙停机、变频器跳闸、精密仪器误动作&#xff0c;查到最后往往都是电网电压跌了那么零点几秒。动态电压恢复器&#xff08;DVR&#xff09;就是专门对付这类问题的装置&#xff0c;在配电端串联在电源和敏感负载之…

作者头像 李华
网站建设 2026/9/9 12:53:46

opencode终端AI编程工具入门到实战:安装配置、免费模型与扩展指南

如果你最近在折腾终端里的 AI 编程工具&#xff0c;opencode、codex、claude code 这几个名字肯定绕不开。我本人花了一整个周末把 opencode 完整走了一遍&#xff0c;包括安装、多模型配置、免费模型接入、skills 扩展、桌面版和编辑器插件&#xff0c;踩了不少坑&#xff0c;…

作者头像 李华