首页
/ FastAPI 挂载 WSGI 应用:用 a2wsgi 的 WSGIMiddleware 集成 Flask、Django 等框架

FastAPI 挂载 WSGI 应用:用 a2wsgi 的 WSGIMiddleware 集成 Flask、Django 等框架

2026-09-06 15:25:25作者:舒璇辛Bertina

本篇技术文章以 FastAPI 官方进阶文档《Including WSGI - Flask, Django, others》为主体,完整讲解如何在 FastAPI(ASGI)应用中挂载一个 WSGI 应用(如 Flask、Django):包括通过 app.mount() 挂载 a2wsgi.WSGIMiddleware 包装的 WSGI 应用、依赖安装、请求路由分发机制、验证方法,以及旧版 fastapi.middleware.wsgi 导入路径的弃用说明。读完后,你可以将一个既有的 WSGI 服务整体接入 FastAPI,使其与 FastAPI 的接口在同一个进程中共存并按路径前缀分流,同时理解这一能力在 FastAPI 源码中的实际落地位置。

背景:为什么需要把 WSGI 应用放进 FastAPI

FastAPI 构建在 Starlette 之上,是一个 ASGI 应用,天然运行在异步(async/await)模型中;而 Flask、Django 等传统框架多数暴露的是 WSGI 接口,是同步调用约定。当你在项目中既有基于 FastAPI 的新 API,又有一段历史悠久的 WSGI 代码需要继续对外提供服务时,最省事的做法就是在一个进程里把两者合并起来:FastAPI 作为“主应用”,WSGI 应用被“挂载”(mount)到某个路径前缀下。

这种“挂载”机制并不是 WSGI 独有的。FastAPI 官方文档 Sub Applications - Mounts 中描述的 app.mount() 用法,可以把任意独立的 ASGI 应用(比如另一个 FastAPI() 实例)挂到指定路径下,每个子应用拥有自己独立的 OpenAPI 和文档 UI。WSGI 应用挂载到 FastAPI 时走的正是同一条 mount 通路,区别只在于挂载的对象是“被 ASGI 化的 WSGI 应用”。

使用 WSGIMiddleware 挂载 WSGI 应用

前置依赖:安装 a2wsgi

官方文档明确要求:使用 WSGIMiddleware 需要先把 a2wsgi 加入项目依赖,例如:

uv add a2wsgi

FastAPI 仓库自身的开发依赖中也可以印证版本要求,pyproject.toml 中声明了:

"a2wsgi >=1.9.0,<=2.0.0",
"flask >=3.0.0,<4.0.0",

也就是说,官方示例所配套的 Flask 为 3.x 版本、a2wsgi 为 1.9.x 以上版本。

完整示例代码

官方示例位于 docs_src/wsgi/tutorial001_py310.py,完整内容如下:

from a2wsgi import WSGIMiddleware
from fastapi import FastAPI
from flask import Flask, request
from markupsafe import escape

flask_app = Flask(__name__)


@flask_app.route("/")
def flask_main():
    name = request.args.get("name", "World")
    return f"Hello, {escape(name)} from Flask!"


app = FastAPI()


@app.get("/v2")
def read_main():
    return {"message": "Hello World"}


app.mount("/v1", WSGIMiddleware(flask_app))

逐段拆解这份示例:

  1. a2wsgi 导入 WSGIMiddleware(第 1 行):这是官方文档强调的正确导入路径。WSGIMiddleware 是一个 ASGI 中间件类,它把传入的 WSGI 可调用对象包装成符合 ASGI 协议的中间件,从而能被 ASGI 服务器(如 Uvicorn)和 FastAPI 的路由系统接管。

  2. 构造 WSGI 应用(第 6 行起):这里是一个最简 Flask 应用,根路由 / 读取查询参数 name(缺省为 "World"),并用 markupsafe.escape 做 HTML 转义后返回纯文本。注意这里的 / 是相对于挂载前缀的路径,实际对外地址是 /v1/

  3. 构造 FastAPI 主应用(第 15 行起):声明了一个普通的 FastAPI 路径操作 GET /v2,返回 JSON。

  4. 挂载 WSGI 应用(第 23 行):

    app.mount("/v1", WSGIMiddleware(flask_app))
    

    这一行是整个文档的核心操作:用 WSGIMiddleware 包一层 Flask 应用,然后 mount 到路径 /v1 之下。挂载后,所有落在 /v1/ 前缀下的请求都会被转发进这个 WSGI 应用,其余请求仍由 FastAPI 处理。

验证运行结果

运行示例应用(例如 uv run fastapi dev main.py,其中 main.py 即上述示例文件)后,两条路径分别由两个框架处理:

  • 访问 http://localhost:8000/v1/:请求进入 Flask,返回纯文本响应:

    Hello, World from Flask!
    
  • 访问 http://localhost:8000/v2:请求进入 FastAPI,返回 JSON 响应:

    {
        "message": "Hello World"
    }
    

此外,由于 Flask 路由读取了 name 查询参数,访问 http://localhost:8000/v1/?name=FastAPI 会得到 Hello, FastAPI from Flask!——这正是查询参数经由 ASGI→WSGI 转换层正常传递的体现。

FastAPI 仓库中也为这个教程提供了自动化测试 tests/test_tutorial/test_wsgi/test_tutorial001.py,它直接用 TestClient 对教程应用发起请求并断言上述两条响应:

def test_flask():
    response = client.get("/v1/")
    assert response.status_code == 200, response.text
    assert response.text == "Hello, World from Flask!"


def test_app():
    response = client.get("/v2")
    assert response.status_code == 200, response.text
    assert response.json() == {"message": "Hello World"}

这组测试从实现层面确认了:挂载后的 WSGI 应用确实能收到请求并返回正确内容,而 FastAPI 侧的接口行为不受影响。

关于已弃用的 fastapi.middleware.wsgi

官方文档特别指出:早期版本中曾推荐使用 fastapi.middleware.wsgi 里的 WSGIMiddleware,该路径现已弃用,建议改用独立的 a2wsgi 包,用法保持不变,只需确保安装了 a2wsgi 并从 a2wsgi 正确导入。

从源码结构看,fastapi/middleware/wsgi.py 的全部内容如今只是一行对 Starlette 实现的转发:

from starlette.middleware.wsgi import (
    WSGIMiddleware as WSGIMiddleware,
)

也就是说,fastapi.middleware.wsgi.WSGIMiddlewarea2wsgi.WSGIMiddleware 在能力上已趋同,官方统一指向社区独立维护的 a2wsgi 包,避免了在框架内部重复维护一份 WSGI 桥接逻辑。如果你维护的是旧项目,迁移成本很低:把导入语句从 fastapi.middleware.wsgi 换成 a2wsgi 即可,app.mount(path, WSGIMiddleware(wsgi_app)) 的调用方式完全一致。

挂载行为与路径前缀的工作方式

mount 的语义与 Sub Applications - Mounts 中挂载独立 FastAPI 子应用时一致:在某个特定路径下放入一个“完全独立”的应用,由它负责处理该前缀下的一切请求。以本教程为例,可以把路由分发理解为:

请求路径 处理者 说明
/v1//v1/?name=xxx Flask(经 WSGIMiddleware 包装) 前缀 /v1 之后的剩余路径交给 WSGI 应用路由
/v2/docs/openapi.json FastAPI 主应用 其余所有未挂载路径由主应用及其文档 UI 处理

需要注意的细节:

  • 挂载路径带斜杠语义:Flask 路由声明为 /,对外访问的是 /v1/,与官方测试用例 client.get("/v1/") 中的写法一致。
  • WSGI 应用是同步执行的:WSGIMiddleware 负责在 ASGI 的异步上下文中桥接 WSGI 的同步调用约定,因此 Flask/Django 这类同步框架不需要改造即可接入,也不会阻塞 FastAPI 中其他异步接口的并发能力。
  • 子应用拥有独立路由:被挂载的 WSGI 应用有自己的 URL 规则体系(如 Flask 的 @flask_app.route),FastAPI 的 OpenAPI 文档并不会把这些 WSGI 接口纳入 /openapi.json——它们对该主应用的文档 UI 来说是“黑盒”。若需要文档能力,可参考 Sub Applications - Mounts 中挂载独立 FastAPI 子应用的做法,让子应用自行暴露 /docs

与反向代理场景的组合使用

如果你的 FastAPI 主应用本身部署在反向代理(Traefik、Nginx 等)后面,官方文档 Behind a Proxy 介绍了 ASGI 规范中的 root_path 机制:代理剥掉路径前缀(例如 /api/v1)后再转发给 Uvicorn,而通过 --root-pathFastAPI(root_path="/api/v1") 告知应用该前缀,使重定向与文档 UI 生成的 URL 正确。该文档还明确说明:在使用 root_path 的同时挂载子应用是常规支持场景,“FastAPI will internally use the root_path smartly, so it will just work”。同理,挂载的 WSGI 应用位于 root_path 之下的某个前缀中,代理只需要把完整请求转发给主应用,mountroot_path 的组合即按预期工作。

小结

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