Python 我如何在Flask/WSGI服务器中使用应用程序工厂,为什么它可能不安全? 关于应用程序可调用、WSGI服务器和Flask循环导入的问题

Python 我如何在Flask/WSGI服务器中使用应用程序工厂,为什么它可能不安全? 关于应用程序可调用、WSGI服务器和Flask循环导入的问题,python,flask,wsgi,Python,Flask,Wsgi,我(可能)糊涂了。我想安全地创建Flask/WSGI应用程序 并且仍然能够在WSGI服务器中轻松使用它们 tl;博士 我可以安全地避免在导入init时创建应用程序吗 建议)以后再创建(即使用工厂方法) 如何使该应用程序与WSGI服务器配合使用?尤其地 当我传入配置和其他设置时,不拉它们 来自ENV 例如: def make_app(configdict, appname): app = Flask(appname) app.config.update(configdict)

我(可能)糊涂了。我想安全地创建Flask/WSGI应用程序 并且仍然能够在WSGI服务器中轻松使用它们

tl;博士

  • 我可以安全地避免在导入init时创建应用程序吗 建议)以后再创建(即使用工厂方法)

  • 如何使该应用程序与WSGI服务器配合使用?尤其地 当我传入配置和其他设置时,不拉它们 来自ENV

  • 例如:

    def make_app(configdict, appname):
        app = Flask(appname)
        app.config.update(configdict)
        init_db(configdict)
        set_app_in_global_namespace(app)
    
        #importing now will allow from pkg import app        
        from mypackage import views
    
        return app
    
    我想使用上面的“工厂”,因为我想轻松地控制测试等的配置

    然后我大概想创建一个wsgi.py模块,将应用程序提供给wsgi服务器

    所以最终事情看起来有点像这样

    init.py::

    app = None
    
    def make_app(configdict, appname):
        flaskapp = Flask(appname)
        flaskapp.config.update(configdict)
        init_db(configdict)
    
        global app
        app = flaskapp    
    
        #importing now will allow from pkg import app        
        from mypackage import views
    
        return flaskapp
    
    py::

    from mypackage import app
    
    app = make_app(configfromsomewhere, "myname")
    
    uWSGI::

    uwsgi --module=mypackage.wsgi:app
    
    但是wsgi.py仍然不是像wsgi.py--settings=x--host=10.0.0.1那样我可以调用的东西 所以我真的不知道如何传递配置

    我这么问是因为虽然这看起来。。。好啊也有点乱

    当一切都在环境中时,生活就容易多了

    不仅如此,而且:

    那么,使用应用程序工厂有什么不安全之处呢 此处给出的建议
    _ 是::

    回复:2,很明显,路线装饰者期望从 实例化的应用程序,没有它们无法运行。那很好

    回复:1.,好的,我们需要正确的名称。但什么是不安全的?及 为什么?如果未初始化,则导入和使用应用程序是否不安全? 嗯,它会断裂,但这并不不安全。 这是被吹嘘的本地线程吗?可能地但如果我在拔 应用程序实例故意从随机模块中删除,我认为会有麻烦

    含义-我们不从视图以外的任何地方引用应用程序对象-本质上,我们保持模块化良好且紧凑,并进行传递 dicts、错误对象,甚至WebObs

    根据,应用程序工厂是好的,因为:

  • 测试。您可以使用具有不同设置的应用程序实例来测试每个案例

  • 多个实例。假设您想要运行同一应用程序的不同版本。当然,您可以在Web服务器中设置具有不同配置的多个实例,但是如果使用工厂,您可以在同一应用程序进程中运行同一应用程序的多个实例,这非常方便

  • 但是,正如文档部分中所述,如果您使用的是应用程序工厂,则不会自动调用函数
    before\u request()
    before\u request()

    在接下来的段落中,我将展示我是如何将应用程序工厂模式用于uWSGI应用程序服务器和nginx的(我只使用了这些,但我可以尝试帮助您使用其他服务器进行配置)

    应用程序工厂 因此,假设您的应用程序位于文件夹your application中,其中有
    \uuuu init\uuuuuuuy.py
    文件:

    import os
    from flask import Flask
    
    def create_app(cfg=None):
        app = Flask(__name__)
    
        load_config(app, cfg)
    
        # import all route modules
        # and register blueprints
    
        return app
    
    def load_config(app, cfg):
        # Load a default configuration file
        app.config.from_pyfile('config/default.cfg')
    
        # If cfg is empty try to load config file from environment variable
        if cfg is None and 'YOURAPPLICATION_CFG' in os.environ:
            cfg = os.environ['YOURAPPLICATION_CFG']
    
        if cfg is not None:
            app.config.from_pyfile(cfg)
    
    现在,您需要一个文件来创建应用程序的实例:

    from yourapplication import create_app
    
    app = create_app()
    
    if __name__ == "__main__":
        app.run()
    
    在上面的代码中,我假设有一个环境变量设置了配置文件的路径,但是您可以将配置路径指定给工厂,如下所示:

    app = create_app('config/prod.cfg')
    
    def load_config(app, env):
        app.config.from_pyfile('config/default.cfg')
    
        var = "YOURAPPLICATION_ENV"
        if env is None and var in os.environ:
            env = os.environ[var]
    
        if env in CONFIG_FILES:
            app.config.from_pyfile(CONFIG_FILES[env])
    
    [uwsgi]
    plugins=python
    vhost=true
    socket=/tmp/yourapplication.sock
    env = YOURAPPLICATION_ENV=production
    logto = /var/www/yourapplication/logs/uwsgi.log
    
    或者,您可以使用类似于字典的工具,其中包含环境和相应的配置文件:

    CONFIG_FILES = {'development': 'config/development.cfg',
                    'test'       : 'config/test.cfg',
                    'production' : 'config/production.cfg' }
    
    在这种情况下,
    load\u config
    函数如下所示:

    app = create_app('config/prod.cfg')
    
    def load_config(app, env):
        app.config.from_pyfile('config/default.cfg')
    
        var = "YOURAPPLICATION_ENV"
        if env is None and var in os.environ:
            env = os.environ[var]
    
        if env in CONFIG_FILES:
            app.config.from_pyfile(CONFIG_FILES[env])
    
    [uwsgi]
    plugins=python
    vhost=true
    socket=/tmp/yourapplication.sock
    env = YOURAPPLICATION_ENV=production
    logto = /var/www/yourapplication/logs/uwsgi.log
    
    Nginx和uWSGI 以下是nginx的配置文件示例:

    server {
        listen             80;
        server_name        yourapplication.com;
        access_log         /var/www/yourapplication/logs/access.log;
        error_log          /var/www/yourapplication/logs/error.log;
    
        location / {
            try_files $uri @flask;
        }
    
        location @flask {
            include        uwsgi_params;
            uwsgi_pass     unix:/tmp/yourapplication.sock;
    
            # /env is the virtualenv directory
            uwsgi_param    UWSGI_PYHOME                /var/www/yourapplication/env;
    
            # the path where the module run is located
            uwsgi_param    UWSGI_CHDIR                 /var/www/yourapplication;
    
            # the name of the module to be called
            uwsgi_param    UWSGI_MODULE                run;
    
            # the variable declared in the run module, an instance of Flask
            uwsgi_param    UWSGI_CALLABLE              app;
        }
    }
    
    uWSGI配置文件如下所示:

    app = create_app('config/prod.cfg')
    
    def load_config(app, env):
        app.config.from_pyfile('config/default.cfg')
    
        var = "YOURAPPLICATION_ENV"
        if env is None and var in os.environ:
            env = os.environ[var]
    
        if env in CONFIG_FILES:
            app.config.from_pyfile(CONFIG_FILES[env])
    
    [uwsgi]
    plugins=python
    vhost=true
    socket=/tmp/yourapplication.sock
    env = YOURAPPLICATION_ENV=production
    logto = /var/www/yourapplication/logs/uwsgi.log
    
    如何在请求前()和请求后()使用
    这些函数的问题在于,如果您在其他模块中调用它们,则在应用程序实例化之前,无法导入这些模块。同样,政府也有话要说:

    缺点是在导入时不能在蓝图中使用应用程序对象。但是,您可以在请求中使用它。如何使用配置文件访问应用程序?使用当前的应用程序:


    或您可以考虑,然后您可以导入类,而不存在任何存在的烧瓶实例,因为类扩展只会在创建后使用Fulk实例。

    所以,刚刚意识到这一点-我可以通过使用CurrnEnApp->在其他位置使用应用程序。这似乎还有很长的路要走-我可能会被要求完全避免路由装饰器,并手动设置路由-然后我可以忽略导入顺序-我认为?您可以使用类作为视图,而不是带有装饰器的函数。看一看,和。另外,请看一看并了解如何组织应用程序。“但是,请注意,如果您使用的是测试请求上下文,则对于after_request()函数,before_request()函数不会自动调用相同的函数。”no before/after request函数适用于测试请求上下文,而不是工厂函数。但是,您仍然需要将其挂接到工厂或在实例化应用程序后定义它们。在设置其他扩展(如Flask Admin)时使用应用程序配置如何?例如,如果我想让我的apps config的值指示管理员视图是否显示编辑选项(can\u create=current\u app.config.get('SOME\u config'))?或者对于可以从多个位置调用但需要应用程序配置的实用程序功能?在这种情况下,当前的应用程序是空的,因为我们在请求上下文之外。“有什么办法处理这样的案件吗?”罗伯托说。