flask多应用挂载:DispatcherMiddleware
问题背景
有一段祖传代码,近期需要重构部分接口,会对整体代码结构有很大改动, 于是按照独立的flask 构建了新版本的接口通过DispatcherMiddleware 进行整合.
样例代码如下
# demo.py
from flask import Flask
from werkzeug.middleware.dispatcher import DispatcherMiddleware
api_v1 = Flask("api_v1")
api_v2 = Flask("api_v2")
app = DispatcherMiddleware(api_v1, {"/v2": api_v2})
# .flaskenv
FLASK_APP=demo:api_v1
本地调试 使用flask run
运行服务
Error: A valid Flask application was not obtained from "demo:app"
flask run
会去找Flask对象来执行,上面的DispatcherMiddleware 已经不是.
先看看DispatcherMiddleware代码:
class DispatcherMiddleware(object):
"""Combine multiple applications as a single WSGI application.
Requests are dispatched to an application based on the path it is
mounted under.
:param app: The WSGI application to dispatch to if the request
doesn't match a mounted path.
:param mounts: Maps path prefixes to applications for dispatching.
"""
def __init__(self, app, mounts=None):
self.app = app
self.mounts = mounts or {}
def __call__(self, environ, start_response):
script = environ.get("PATH_INFO", "")
path_info = ""
while "/" in script:
if script in self.mounts:
app = self.mounts[script]
break
script, last_item = script.rsplit("/", 1)
path_info = "/%s%s" % (last_item, path_info)
else:
app = self.mounts.get(script, self.app)
original_script_name = environ.get("SCRIPT_NAME", "")
environ["SCRIPT_NAME"] = original_script_name + script
environ["PATH_INFO"] = path_info
return app(environ, start_response)
翻看 Flask代码:
class Flask(_PackageBoundObject):
# 此处省略N行....
def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in
:meth:`__call__` so that middlewares can be applied without
losing a reference to the app object. Instead of doing this::
app = MyMiddleware(app)
It's a better idea to do this instead::
app.wsgi_app = MyMiddleware(app.wsgi_app)
Then you still have the original application object around and
can continue to call methods on it.
"""
pass
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
下面这段话 指出了挂载应用的正确姿势app.wsgi_app = MyMiddleware(app.wsgi_app)
, 这样我们可以继续使用原始的app 对象
"""The actual WSGI application. This is not implemented in
:meth:`__call__` so that middlewares can be applied without
losing a reference to the app object. Instead of doing this::
app = MyMiddleware(app)
It's a better idea to do this instead::
app.wsgi_app = MyMiddleware(app.wsgi_app)
Then you still have the original application object around and
can continue to call methods on it.
"""
最后的代码如下:
from flask import Flask
from werkzeug.middleware.dispatcher import DispatcherMiddleware
api_v1 = Flask("api_v1")
api_v2 = Flask("api_v2")
# app = DispatcherMiddleware(api_v1, {"/v2": api_v2})
api_v1.wsgi_app=DispatcherMiddleware(api_v1.wsgi_app, {"/v2": api_v2})
#api_v1.run()
运行 flask run
服务正常
参考
本文来自博客园,作者:码上的生活,转载请注明原文链接:https://www.cnblogs.com/zyl007/p/14756795.html