DispatcherMiddleware用于多Flask应用共用WSGI服务器,路径必须以/开头和结尾(如"/api/"),子应用路由写相对路径,静态文件需额外处理,Gunicorn入口须为application变量。

多个Flask应用共用一个WSGI服务器时,DispatcherMiddleware怎么配
直接用 DispatcherMiddleware 套娃就行,但必须注意路径结尾斜杠——它不自动补,也不忽略。比如把 app1 挂在 /admin 下,实际访问地址是 /admin/(带尾部斜杠),而 /admin(无尾斜杠)会 404。
- 每个子应用的
url_prefix必须以/开头且以/结尾,例如"/api/"、"/blog/" -
DispatcherMiddleware的第一个参数是“主应用”(可为None),后续元组里路径和应用要一一对应,顺序影响匹配优先级 - 子应用内部路由仍写相对路径(如
@app.route("/users")),不用加前缀;前缀由DispatcherMiddleware在 WSGI 层剥离 - 别在子应用里设
static_url_path或template_folder为绝对路径,否则静态文件可能 404
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.serving import make_server
<p>app1 = Flask(<strong>name</strong>)
app2 = Flask(<strong>name</strong>)</p><p>@app1.route("/health")
def health(): return "app1 ok"</p><p>@app2.route("/status")
def status(): return "app2 ok"</p><h1>注意:"/api/" 和 "/web/" 都以 / 开头和结尾</h1><p>application = DispatcherMiddleware(None, {
"/api/": app1,
"/web/": app2,
})为什么 url_prefix 不能只靠 Blueprint 解决多 App 隔离
Blueprint 的 url_prefix 只解决单个 Flask 实例内的路由分组,不是跨应用隔离方案。多个独立 Flask() 实例之间完全不共享配置、请求上下文、扩展实例(比如 SQLAlchemy),强行塞进同一个 app 会导致初始化冲突、中间件错乱、日志混杂。
- 若用 Blueprint 模拟多 App,所有子模块得共用一个
app.config、一个app.extensions,改一个地方可能崩全站 - 不同 App 往往需要不同数据库连接、不同中间件(如一个要 JWT,一个要 Basic Auth),Blueprint 无法隔离这些
- 部署时没法单独启停或扩缩某个子 App;而用
DispatcherMiddleware后,每个子 App 是独立对象,可分别测、分别打日志、分别加监控钩子
DispatcherMiddleware 下静态文件 404 怎么查
根本原因是 Werkzeug 默认不处理子应用的静态路由——它只认根应用的 static_folder。子应用的 static_url_path 和 static_folder 会被忽略,除非手动挂载。
- 最简单做法:统一把所有静态资源放在 Nginx/Apache 下反代,让 Web 服务器直接服务
/static/、/api/static/这类路径 - 如果必须用 Python 服务静态文件,得给每个子应用额外包一层
SharedDataMiddleware,且路径要和DispatcherMiddleware的挂载点对齐 - 检查
app.static_url_path是否被意外覆盖(比如设成""或"/"),这会让url_for("static", filename="...")生成错误路径 - 调试时打印
request.base_url和request.url,确认浏览器请求的完整 URL 是否匹配DispatcherMiddleware定义的前缀
用 Gunicorn 部署时 DispatcherMiddleware 的入口点写法
Gunicorn 只认一个可调用对象,所以不能直接传 app1 或 app2,必须传最终组装好的 application(即 DispatcherMiddleware 实例)。
- 入口文件(如
wsgi.py)末尾必须有application这个变量名,Gunicorn 才能识别:gunicorn wsgi:application - 不要写
if __name__ == "__main__":启动开发服务器,Gunicorn 不执行该分支 - 子应用里的
app.run()必须删干净,否则部署时会报Address already in use - 若用
flask run调试,需改用--app wsgi:application并确保wsgi.py在 Python path 中
复杂点在于,每个子 App 的错误日志默认都打到同一个 stderr,没上下文区分。真要隔离,得在每个子 App 初始化时自己配 logging.Handler,加上前缀标签——这点容易被忽略。











