FastAPI 子应用

3/8/2025

本文谈论 FastAPI 子应用。很可能你可能直接用上,但是有些开源项目用上了。这里简单的探索一下。

一、谁在用?

open-webui 使用就是 子应用 模式。开始之前希望读者对 FastAPI 框架有一些基本的认识。

image.png

image.png

二、与嵌套路由的区别

  • 路由嵌套使用 APIRouter定义路由 组织,使用 app.add_router 添加路由。
  • 子应用 FastAPI 添加应用,使用 app.mount 挂载子应用。

三、开始带有子应用的 FastAPI 应用

cd your dir
poetry init

poetry add fastapi[all] # 这里我们安装全功能版本
[tool.poetry.dependencies]
fastapi = {extras = ["all"], version = "^0.115.5"}

四、定义和挂载方式

  • 在 fastapi 中 FastAPI 定义 app
  • 在 fastapi 使用 app.mount 挂载子 app
  • 使用 lifespan 定义生命周期
  • 在生命周期中定义 state 状态
  • 定义子应用

分别子应用的表现出来的特性。

五、主应用:main.py

from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from middlewares.PM_Global import PM_Global

from user import app as user_app

@asynccontextmanager
async def lifespan(app: FastAPI): 
    app.state.config = {"app_name": "FastAPI Main"} # 在 app state 中定义 config 配置状态
    print("Global  life span")
    yield


app = FastAPI(lifespan=lifespan) # 定义全局 app, 带有 lifespan

app.add_middleware(PM_Global, name="PM_Global") # 注册全局中间件

app.mount("/api/user", user_app) # 使用 app mount 挂载 user_app

@app.get("/")
async def root(request: Request):
    print("main",request.app.state.__dict__) # 在接口中方位 app.state 的状态(状态成字典的形式)
    return {"message": "Hello World"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", port=8000, reload=True)

使用 class 定义全局使用 PM_Global 中间件

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware

class PM_Global(BaseHTTPMiddleware):
    def __init__(self, app, name: str):
        super().__init__(app)
        self.name = name

    async def dispatch(self, request: Request, call_next):
        print(f"Executing middleware Gloal: {self.name}") # 全局输出字符串
        # 调用下一个中间件或最终的请求处理器
        response = await call_next(request)
        return response

中间件的内容:很简单输出带有 Global 字样的字符串.

六、子应用 user.py

from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from middlewares.PM_Sub1 import PM_Sub1

@asynccontextmanager
async def lifespan(app: FastAPI):
    print("PM_Sub1  life span") # spanlife 生命周期
    yield

app = FastAPI(lifespan=lifespan)

app.add_middleware(PM_Sub1, name="PM_Sub1") # 添加 user 的中间件

@app.get("/")
def read_root(request: Request):
    print(request.app.state.__dict__) # 使用 request 访问 app 的状态。
    return {"Hello": "World"}

七、启动 fastapi

poetry run main.py

image.png

我们看到 全局中间件 已经执行了。下面看看子应用 user

image.png

看到全局的中间件先输出,子应用的中间件后输出。从两者的表现是中间件在应用中是独立的。

八、访问 docs

访问一下 docs, 子应用有自己的 docs 地址:

  • http://127.0.0.1:8000/docs

image.png

  • http://127.0.0.1:8000/api/user/docs

image.png

九、查看 state 状态

  • 访问接口 /

image.png

发现 主应用 的状态能够正常访问。

  • 访问 /api/user

image.png

副应用由于没有添加数据,所有访问是一个空字典。由此也可以从实践中得出,子应用与主应用 state 是独立的。我们不能直接在子应用中获取主应用的路径。

十、使用场景

  • 多模块开发:用户模块、文章模块
  • 版本控制: v1/v2 不同的版本
  • 需要演进(微服务)的项目

如果的你项目足够小,使用子应用可能会过渡设计。子应用适合有一定的的规模的项目。同时便于团队合作。

十一、小结

本文主要探索 FastAPI 主要子应用的基本使用和适用场景。子应用使用 FastAPI 构造,需要 mount 进行挂载子应用。子应用在中间件状态管理文档生命周期等独立。子应用在多模块开发和版本管理时有较大的用处。