如何在Flask / WSGI服务器中使用app-factory以及为什么它不安全?

时间:2012-12-06 19:44:11

标签: python flask wsgi

关于app callables,WSGI服务器和Flask循环导入的问题

我(可能)感到困惑。我想安全地创建Flask / WSGI应用程序 来自app-factory,并且仍然可以轻松地在WSGI服务器中使用它们。

TL;博士

  1. 我可以安全地避免在导入 init 时创建应用(如 推荐)而是稍后创建它(即使用工厂方法)

  2. 如何使该应用程序与WSGI服务器完美配合?特别 当我传递配置和其他设置而不是拉他们 来自ENV

  3. 例如::

    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服务器。

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

    初始化的.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
    

    wsgi.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 所以我真的不知道如何在中传递配置

    我在问,因为虽然这看起来......好吧......它也有点乱。

    当一切都在ENV中时,生活变得更加轻松。

    不仅是,而且:

    那么使用app-factory

    是不安全的

    给出的建议here <http://flask.pocoo.org/docs/patterns/packages> _  是::

    1. the Flask application object creation has to be in the
    __init__.py file. That way each module can import it safely and
    the __name__ variable will resolve to the correct package.
    
    2. all the view functions (the ones with a route() decorator on
      top) have to be imported in the __init__.py file. Not the object
      itself, but the module it is in. Import the view module after
      the application object is created.
    

    re:2。,显然路线装饰者需要某些能力 一个实例化的应用程序,没有它们就无法运行。太好了。

    重新:1。,我们需要名称正确。但什么是不安全的?和 为什么?如果未初始化,导入和使用应用程序是否不安全? 好吧它会破裂,但那并不安全。 这是一个被大肆吹嘘的线程本地?有可能。但如果我采摘 随机模块中的应用程序实例我应该会遇到麻烦。

    含义 - 我们不会从视图以外的任何内容引用app对象 - 基本上我们保持模块化的美观和紧凑,并且传递 dicts,错误对象,甚至是WebObs。

    http://flask.pocoo.org/docs/patterns/appdispatch http://flask.pocoo.org/docs/deploying/#deployment http://flask.pocoo.org/docs/patterns/packages/#larger-applications http://flask.pocoo.org/docs/becomingbig

1 个答案:

答案 0 :(得分:22)

根据Flask Documentation,申请工厂很好,因为:

  
      
  1. 测试。您可以使用不同设置的应用程序实例来测试每种情况。

  2.   
  3. 多个实例。想象一下,您想要运行同一应用程序的不同版本。当然,您可以在Web服务器中设置多个具有不同配置的实例,但是如果您使用工厂,则可以在同一个应用程序进程中运行同一应用程序的多个实例,这样可以很方便。

  4.   

但是,正如文档的Other Testing Tricks部分所述,如果您正在使用应用程序工厂,则不会自动调用函数before_request()after_request()

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

应用工厂

所以,假设您的应用程序位于您的应用程序文件夹中,并且其中包含__init__.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')

或者,您可以使用包含环境和相应配置文件的字典:

CONFIG_FILES = {'development': 'config/development.cfg',
                'test'       : 'config/test.cfg',
                'production' : 'config/production.cfg' }

在这种情况下,load_config函数将如下所示:

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])

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配置文件如下所示:

[uwsgi]
plugins=python
vhost=true
socket=/tmp/yourapplication.sock
env = YOURAPPLICATION_ENV=production
logto = /var/www/yourapplication/logs/uwsgi.log

如何使用before_request()after_request()

这些函数的问题在于,如果您在其他模块中调用它们,则在实例化应用程序之前无法导入这些模块。同样,documentation有话要说:

  

缺点是您无法在导入时在蓝图中使用应用程序对象。但是,您可以在请求中使用它。如何使用配置访问应用程序?使用current_app:

from flask import current_app, Blueprint, render_template
admin = Blueprint('admin', __name__, url_prefix='/admin')

@admin.route('/')
def index():
    return render_template(current_app.config['INDEX_TEMPLATE'])

或者您可以考虑creating an extension,然后您可以在没有任何Flask实例的情况下导入该类,因为类扩展只会在创建后使用Flask实例。