• flask run 与 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 服务正常

    参考

    1. https://github.com/pallets/flask/issues/3256
    2. https://stackoverflow.com/questions/52483686/why-do-i-get-the-error-a-valid-flask-application-was-not-obtained-from-when
    3. https://flask.palletsprojects.com/en/1.0.x/api/#flask.Flask.wsgi_app
  • 相关阅读:
    1.18
    人月神话读后感
    疯狂学java的第45天
    学Java的第46天
    JAVA学习日记150720
    JAVA学习日记140719
    JAVA学习日记160721
    JAVA学习日记130718
    Windows DOS窗体下Oracle 数据库的导入导出(IMP/EXP)命令
    IntelliJ IDEA自动清除没用的import
  • 原文地址:https://www.cnblogs.com/zyl007/p/14756795.html
Copyright © 2020-2023  润新知