FastAPI 子应用挂载实战:用 Mount 构建多套独立 OpenAPI 与文档 UI
本篇指南基于 FastAPI 官方文档《Sub Applications - Mounts》,讲解如何通过 app.mount() 把一个独立的 FastAPI 子应用挂载到主应用下的某个路径前缀(如 /subapi),使两套应用各自拥有独立的 OpenAPI Schema 与独立的 Swagger UI 文档界面。读完后,你将掌握:多应用共存的路由组织方式、挂载后各应用文档 URL 的正确形态,以及底层 ASGI 规范中 root_path 机制如何自动为子应用注入挂载前缀,使其文档 UI、OpenAPI servers 字段均无需手动配置即可正确工作。
为什么要挂载子应用(Sub Applications)
当项目中确实需要两个相互独立的 FastAPI 应用——它们各自拥有独立的 OpenAPI Schema、独立的 Swagger UI / ReDoc 文档页面——而不是仅仅把一些路由 include_router 到同一个应用里时,可以使用一个"主应用 + 挂载一个或多个子应用"的结构。
"挂载"(Mount)的含义是:把一个完全独立的应用添加到一个特定的路径前缀下,从此该路径下的所有请求都由这个子应用内部声明的 path operations 来处理。与 APIRouter 不同,被挂载的对象是一个完整的 FastAPI 实例:它有自己的标题、元数据、中间件、异常处理、启动/关闭事件和文档路由,而不仅仅是路由表。
完整可运行示例
下面的代码取自仓库示例文件 tutorial001_py310.py,可直接复制到本地运行(要求 Python 3.10+ 与已安装的 FastAPI、Uvicorn):
from fastapi import FastAPI
app = FastAPI()
@app.get("/app")
def read_main():
return {"message": "Hello World from main app"}
subapi = FastAPI()
@subapi.get("/sub")
def read_sub():
return {"message": "Hello World from sub API"}
app.mount("/subapi", subapi)
整个示例分成三步,下面逐段拆解。
第一步:创建顶层主应用及其路由
先创建主应用 app,并为其声明属于主应用自己的 path operations:
app = FastAPI()
@app.get("/app")
def read_main():
return {"message": "Hello World from main app"}
这里主应用只声明了 /app 这一个路径操作。挂载子应用后,主应用的自动文档里只会出现 /app,不会混入子应用的路由。
第二步:创建子应用及其路由
子应用 subapi 就是另一个标准的 FastAPI 实例——你可以像创建任何应用一样创建它,只是稍后它会成为被"挂载"的那个:
subapi = FastAPI()
@subapi.get("/sub")
def read_sub():
return {"message": "Hello World from sub API"}
注意 subapi 内部的路由声明为 /sub,而不是 /subapi/sub——你不需要在子应用里重复书写挂载前缀,前缀由挂载动作自动施加。
第三步:把子应用挂载到主应用
在顶层应用 app 中调用 app.mount("/subapi", subapi),将子应用挂载到路径 /subapi:
app.mount("/subapi", subapi)
调用之后:
- 主应用继续处理不属于
/subapi前缀的请求(如GET /app); - 所有以
/subapi开头的请求都会被转发给subapi实例,由它内部的路由(如/subapi/sub)处理; - 主应用和子应用各自的
/docs、/redoc、/openapi.json路由互不干扰。
运行并查看两套自动 API 文档
启动服务(FastAPI CLI 会由 uv 管理虚拟环境并运行 Uvicorn):
$ uv run fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
然后打开主应用文档 http://127.0.0.1:8000/docs,你会看到主应用的自动 API 文档,其中只包含它自己的 path operation(/app):
再打开子应用的文档 http://127.0.0.1:8000/subapi/docs,会看到子应用自己的自动文档,其 path operation 全部带上了正确的子路径前缀 /subapi:
分别在两个 UI 中尝试调用接口,二者都能正常工作,因为浏览器会分别与各自具体的应用/子应用通信,请求路径天然携带了对应前缀。
技术细节:ASGI 的 root_path 机制
上面文档 URL 自动带上 /subapi 前缀并不是巧合,其底层依赖 ASGI 规范定义的 root_path 机制。当你按上述方式挂载子应用时,FastAPI(经由 Starlette 的 Mount 层)会在转发请求时把挂载路径写入请求的 ASGI scope 中,即 scope["root_path"]。子应用读取到这个值后,就知道应为自己的文档 UI 使用该路径前缀。
从源码中可以印证这一点:
- 在 fastapi/applications.py 的
setup()方法中,Swagger UI 路由处理器在每次请求时都会执行root_path = req.scope.get("root_path", "").rstrip("/"),然后拼出openapi_url = root_path + self.openapi_url再生成 HTML。这就是子应用的/subapi/docs能正确指向/subapi/openapi.json的原因。 - 同文件中(fastapi/applications.py),
/openapi.json的响应处理也会读取root_path,并在root_path_in_servers=True(默认值)时把它注入 OpenAPI Schema 的servers字段列表头部,使文档中的 "Try it out" 请求指向正确的前缀。 - 在 fastapi/routing.py 中,请求处理也会读取
request.scope.get("root_path", "")作为挂载路径前缀参与端点上下文(如错误信息中的完整路径展示),说明root_path在路由层同样被感知。 - 另外,
FastAPI(root_path=...)构造参数允许你显式设置根路径:在 fastapi/applications.py 的__call__中,若实例配置了root_path,会直接写入scope["root_path"]。这个参数面向"应用位于反向代理之后、代理吞掉了前缀"的场景,详见官方文档 Behind a Proxy。对于 mount 出来的子应用,root_path由挂载层自动处理,无需手动设置。
由于 root_path 是在 ASGI scope 层逐层传递的,子应用自己也可以继续 mount 更深层的子应用,前缀会逐级叠加,一切照常工作——FastAPI 自动处理了所有这些层级的 root_path。
使用建议与边界
- 何时用 mount:当两个应用需要独立的 OpenAPI 输出、独立的文档页、独立的中间件/事件/标题(例如按业务域或按版本拆分的微服务进程内组合)时;如果只是想在同一应用里组织路由,优先使用
include_router。 - 路径声明不要重复前缀:子应用内部始终声明不带挂载前缀的路由(
/sub而非/subapi/sub),前缀由mount与root_path机制自动完成。 - 文档 URL 形态:主应用文档位于
http://host:port/docs,挂载在/subapi的子应用文档位于http://host:port/subapi/docs,OpenAPI JSON 同理分别为/openapi.json与/subapi/openapi.json。 - 与显式
root_path参数的区别:mount 时前缀来自 ASGI scope,自动且无需配置;FastAPI(root_path=...)是为代理环境手动声明前缀的手段。两者可共存但场景不同,更多用法见 Behind a Proxy。
相关仓库路径
- 示例代码:docs_src/sub_applications/tutorial001_py310.py
- 官方文档原文:docs/en/docs/advanced/sub-applications.md
root_path核心实现:fastapi/applications.py、fastapi/routing.py- 延伸阅读(代理场景):docs/en/docs/advanced/behind-a-proxy.md
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

