news 2026/9/5 19:14:12

FastAPI 自定义文档 UI 静态资源:换 CDN 与完全自托管 Swagger UI / ReDoc

作者头像

张小明

前端开发工程师

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

FastAPI 自定义文档 UI 静态资源:换 CDN 与完全自托管 Swagger UI / ReDoc

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

FastAPI 自动生成的 API 文档(Swagger UI 与 ReDoc)默认从公共 CDN 加载 JavaScript 和 CSS 文件,这意味着文档页面在离线环境、内网环境或 CDN 被限制的部署场景下可能无法使用。本文基于官方文档docs/de/docs/how-to/custom-docs-ui-assets.md(对应 示例源码 1、示例源码 2),完整讲解两种替代方案:替换为自定义 CDN的静态资源地址,以及把 JS/CSS 文件下载到本地、由 FastAPI 应用自身托管。读完本篇,你将能够离线运行 FastAPI 的交互式文档,并理解get_swagger_ui_html等内部函数是如何拼装文档页面的。

默认行为:文档资源来自 CDN

FastAPI 的/docs(Swagger UI)与/redoc(ReDoc)页面本身是服务端动态生成的 HTML,但页面里引用的浏览器端资源(Swagger UI 的 JS 与 CSS、ReDoc 的 JS)默认由 CDN 提供。从源码 fastapi/openapi/docs.py 可以看到默认值:

  • get_swagger_ui_html()的默认参数:swagger_js_url为 jsDelivr CDN 上的swagger-ui-dist@5/swagger-ui-bundle.jsswagger_css_url为同版本的swagger-ui.css
  • get_redoc_html()的默认参数:redoc_js_url为 jsDelivr CDN 上的redoc@2/bundles/redoc.standalone.js

因此只要应用能访问外网 CDN,文档页面开箱即用;而本文要解决的正是“不能用默认 CDN”的场景。

方案一:使用自定义 CDN

假设你想改用另一个 CDN(例如unpkg.com)。这在某些公共 CDN 域名被限制或不可达的网络环境下特别有用。完整示例见 docs_src/custom_docs_ui/tutorial001_py310.py。

第一步:禁用自动文档

自动生成的/docs/redoc路由默认使用默认 CDN,所以第一步是创建FastAPI应用时把它们的 URL 设为None

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)

从源码结构看,fastapi/applications.py 中setup()方法只在self.openapi_url and self.docs_url同时成立时才注册swagger_ui_html路由(约第 1121 行),redoc_url同理(约第 1149 行)。把docs_url/redoc_url置为None后,这两条自动路由就不会被添加,而openapi_url保持默认/openapi.json,OpenAPI Schema 依旧对外提供。

第二步:创建自定义文档的路径操作

可以复用 FastAPI 的内部函数来生成文档 HTML 页面,并传入自己需要的参数:

  • openapi_url:文档 HTML 页面获取 API OpenAPI Schema 的 URL,可直接用应用属性app.openapi_url
  • title:API 标题(显示在浏览器标签页);
  • 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 的用法类似,只是换用get_redoc_html并传redoc_js_url

@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", )

提示swagger_ui_redirect这个路径操作是配合 OAuth2 使用的辅助页。如果你的 API 集成了 OAuth2 提供商,文档页面可以发起认证并带着凭据返回,Swagger UI 在幕后完成授权流程,但它需要这个“重定向”辅助页面来接收回调参数。这也是为什么自定义文档路由时通常要一并注册它。

第三步:加一个测试用路径操作

为了验证一切正常,可以加一个最简单的业务接口:

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

第四步:测试

启动应用后访问http://127.0.0.1:8000/docs并刷新页面,此时 Swagger UI 的 JS/CSS 已从新的 CDN 加载。可以用浏览器开发者工具的网络面板确认资源请求发往的是你配置的 CDN 域名。

方案二:完全自托管文档的 JavaScript 和 CSS

如果应用需要离线运行(无外网、纯内网、本地网络),最彻底的办法是把文档所需的全部 JS/CSS 下载下来,在同一个 FastAPI 应用里自己托管。完整示例见 docs_src/custom_docs_ui/tutorial002_py310.py。

项目文件结构

假设项目结构如下:

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

新建一个存放静态文件的目录static/

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

下载所需文件

把文档需要的静态文件下载并放入static/目录(浏览器中对资源链接“另存为”即可)。Swagger UI需要两个文件:swagger-ui-bundle.jsswagger-ui-dist@5版本)与swagger-ui.css(同版本);ReDoc需要一个文件:redoc.standalone.jsredoc@2的 bundles 版本)。下载后结构如下:

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

文件版本应与 fastapi/openapi/docs.py 中默认 CDN 参数一致(swagger-ui-dist@5redoc@2),以保证行为与官方默认体验相同。

托管静态文件

  • 导入StaticFiles(FastAPI 直接从 Starlette 转出,见 fastapi/staticfiles.py);
  • StaticFiles()实例 “mount” 到指定路径:
from fastapi.staticfiles import StaticFiles app = FastAPI(docs_url=None, redoc_url=None) app.mount("/static", StaticFiles(directory="static"), name="static")

先验证静态文件可访问

启动应用后访问http://127.0.0.1:8000/static/redoc.standalone.js,应该能看到一份很长的 ReDoc 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")): ...

这就说明应用能正确对外提供静态文件,且文件放对了位置。

禁用自动文档并接入本地资源

与方案一相同,创建应用时设置docs_url=None, redoc_url=None。然后创建自定义文档路径操作,区别在于:swagger_js_urlswagger_css_urlredoc_js_url现在指向自己应用托管的本地路径(相对根路径):

@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}"}

同样保留swagger_ui_redirect路径操作,OAuth2 流程才能完整工作。

离线验证 UI

此时可以断开 Wi-Fi,访问http://127.0.0.1:8000/docs并刷新页面——即使完全没有互联网,文档页面依旧可见、可交互。测试用例 tests/test_tutorial/test_custom_docs_ui/test_tutorial002.py 验证了自托管示例中/docs/redoc与静态文件路径都能被正确访问。

源码级细节:这些内部函数做了什么

理解两个生成函数后,自定义 URL 的原理一目了然(见 fastapi/openapi/docs.py):

  • get_swagger_ui_html()(约第 40 行起)返回一段 HTML 字符串:<link rel="stylesheet" href="{swagger_css_url}"><script src="{swagger_js_url}">就是你在自定义路由里传入的 URL;页面内嵌的SwaggerUIBundle({ url: '{openapi_url}', ... })负责在浏览器端拉取 OpenAPI Schema 并渲染。此外它还会注入swagger_ui_parameters(默认含dom_idlayoutdeepLinking等)以及可选的init_oauth
  • get_redoc_html()(约第 197 行起)生成的页面更简单:一个<redoc spec-url="{openapi_url}">自定义元素加一个<script src="{redoc_js_url}">
  • 注意源码中内嵌 JSON 会经过_html_safe_json()转义<>&(约第 9 行),防止把动态内容注入<script>时产生 HTML 注入问题——这也是官方模板可放心拼入openapi_url等动态值的原因。
  • 从源码结构看,自动文档路由(setup()中)会把请求上下文里的root_path拼到openapi_urloauth2_redirect_url前面(fastapi/applications.py 约第 1121–1158 行),以支持部署在反向代理子路径下。如果你自定义文档路由且应用带有root_path,可参考这一处理方式,为openapi_url手动加上前缀。
  • 默认参数方面,get_swagger_ui_html还支持swagger_favicon_url(默认指向 fastapi.tiangolo.com 的 favicon)与init_oauthget_redoc_html支持with_google_fonts(默认开启 Google Fonts)。若你的环境同样无法访问这些外部地址,可一并传入本地或内网地址,让文档页彻底去外部化。

小结

  • 两种方案共享同一个前置步骤:FastAPI(docs_url=None, redoc_url=None)关闭自动文档路由,再用get_swagger_ui_html/get_redoc_html/get_swagger_ui_oauth2_redirect_html自建/docs/redoc与 OAuth2 重定向路径操作(三个示例路由均带include_in_schema=False,避免文档路由本身出现在 Schema 中)。
  • 换 CDN 只需把swagger_js_urlswagger_css_urlredoc_js_url指向新的 CDN 地址;完全自托管则用StaticFiles挂载本地static/目录,并把这些参数改为本地路径,从而让 API 文档在离线、内网环境中照常工作。
  • 参考材料:文档 docs_src/custom_docs_ui/tutorial001_py310.py、docs_src/custom_docs_ui/tutorial002_py310.py,实现 fastapi/openapi/docs.py、fastapi/applications.py,测试 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/5 19:11:31

SpringBoot+Vue招聘系统实战:架构设计、核心模块与避坑指南

简介&#xff1a;这是一套面向Java全栈开发者与高校计算机专业学生的完整企业级招聘系统实战项目&#xff0c;聚焦人力资源数字化管理场景&#xff0c;解决传统招聘流程中职位发布低效、简历筛选粗放、面试调度混乱等痛点。资源包含前后端分离的完整源码&#xff08;64个Java后…

作者头像 李华
网站建设 2026/9/5 19:08:04

Roo Code 实战:5种AI模式让VS Code里的AI编码助手各就各位

Roo Code 实战&#xff1a;5种AI模式让VS Code里的AI编码助手各就各位 【免费下载链接】Roo-Code Roo Code gives you a whole dev team of AI agents in your code editor. 项目地址: https://gitcode.com/GitHub_Trending/ro/Roo-Code Roo Code 是一款装进 VS Code 的…

作者头像 李华
网站建设 2026/9/5 19:00:04

技术博客应远离虚构:选择可复现的真实项目进行写作

这个标题对应的内容无法按现有要求生成。原因很简单&#xff1a;CSDN 技术博客需要的是可验证、可复现、有明确技术边界的项目内容&#xff0c;比如开源模型、本地部署工具、WebUI、接口服务、批量任务等。而当前标题属于虚构战争题材&#xff0c;且存在明显的地缘、政治或现实…

作者头像 李华