web-dev-qa-db-fra.com

Ajouter un préfixe à tous les itinéraires Flask

J'ai un préfixe que je veux ajouter à chaque itinéraire. À l'heure actuelle, j'ajoute une constante à l'itinéraire à chaque définition. Y a-t-il un moyen de faire cela automatiquement?

PREFIX = "/abc/123"

@app.route(PREFIX + "/")
def index_page():
  return "This is a website about burritos"

@app.route(PREFIX + "/about")
def about_page():
  return "This is a website about burritos"
69
Evan Hahn

La réponse dépend de la façon dont vous servez cette application.

Sous-monté à l'intérieur d'un autre conteneur WSGI

En supposant que vous allez exécuter cette application dans un conteneur WSGI (mod_wsgi, uwsgi, gunicorn, etc.); vous devez réellement mount, à ce préfixe l'application en tant que sous-partie de ce conteneur WSGI (tout ce que WSGI fera parler) et définir votre valeur APPLICATION_ROOT config avec votre préfixe:

app.config["APPLICATION_ROOT"] = "/abc/123"

@app.route("/")
def index():
    return "The URL for this page is {}".format(url_for("index"))

# Will return "The URL for this page is /abc/123/"

Définir la valeur APPLICATION_ROOT config limite simplement le cookie de session de Flask à ce préfixe d'URL. Tout le reste sera automatiquement géré pour vous par les excellentes capacités de traitement WSGI de Flask et Werkzeug.

Un exemple de sous-montage adéquat de votre application

Si vous n’êtes pas sûr de la signification du premier paragraphe, jetez un œil à cet exemple d’application avec Flask monté à l’intérieur:

from flask import Flask, url_for
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/abc/123'

@app.route('/')
def index():
    return 'The URL for this page is {}'.format(url_for('index'))

def simple(env, resp):
    resp(b'200 OK', [(b'Content-Type', b'text/plain')])
    return [b'Hello WSGI World']

app.wsgi_app = DispatcherMiddleware(simple, {'/abc/123': app.wsgi_app})

if __== '__main__':
    app.run('localhost', 5000)

Envoi de requêtes à l'application

Par contre, si vous exécutez votre application Flask à la racine de son conteneur WSGI et lui transmettez ses requêtes par proxy (par exemple, s'il s'agit de FastCGI, ou si nginx est proxy_pass- demande de sous-requête). sur votre serveur autonome uwsgigevent, vous pouvez soit:

  • Utilisez un plan directeur, comme le souligne Miguel dans sa réponse .
  • _/ou utilisez la DispatcherMiddleware de werkzeug (ou la PrefixMiddleware de su27's answer ) pour sous-monter votre application sur le serveur WSGI autonome que vous utilisez. (Voir Un exemple de sous-montage correct de votre application ci-dessus pour connaître le code à utiliser).
59
Sean Vieira

Vous pouvez mettre vos itinéraires dans un plan:

bp = Blueprint('burritos', __name__,
                        template_folder='templates')

@bp.route("/")
def index_page():
  return "This is a website about burritos"

@bp.route("/about")
def about_page():
  return "This is a website about burritos"

Ensuite, vous enregistrez le plan avec l'application en utilisant un préfixe:

app = Flask(__name__)
app.register_blueprint(bp, url_prefix='/abc/123')
71
Miguel

Vous devriez noter que le APPLICATION_ROOT n'est PAS à cette fin.

Tout ce que vous avez à faire est d'écrire un middleware pour apporter les modifications suivantes:

  1. modifiez PATH_INFO pour gérer l'URL préfixée.
  2. modifiez SCRIPT_NAME pour générer l'URL préfixée.

Comme ça:

class PrefixMiddleware(object):

    def __init__(self, app, prefix=''):
        self.app = app
        self.prefix = prefix

    def __call__(self, environ, start_response):

        if environ['PATH_INFO'].startswith(self.prefix):
            environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
            environ['SCRIPT_NAME'] = self.prefix
            return self.app(environ, start_response)
        else:
            start_response('404', [('Content-Type', 'text/plain')])
            return ["This url does not belong to the app.".encode()]

Enveloppez votre application avec le middleware, comme ceci:

from flask import Flask, url_for

app = Flask(__name__)
app.debug = True
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix='/foo')


@app.route('/bar')
def bar():
    return "The URL for this page is {}".format(url_for('bar'))


if __== '__main__':
    app.run('0.0.0.0', 9010)

Visitez http://localhost:9010/foo/bar,

Vous obtiendrez le bon résultat: The URL for this page is /foo/bar

Et n'oubliez pas de définir le domaine des cookies si vous en avez besoin.

Cette solution est donnée par Larivact's Gist . Le APPLICATION_ROOT ne convient pas à ce travail, bien qu'il semble l'être. C'est vraiment déroutant.

27
su27

C'est plus une réponse en python qu'une réponse Flask/werkzeug; mais c'est simple et fonctionne.

Si, comme moi, vous souhaitez que les paramètres de votre application (chargés à partir d'un fichier .ini) contiennent également le préfixe de votre application Flask (pour que la valeur ne soit pas définie lors du déploiement, mais lors de l'exécution), vous pouvez opter pour les éléments suivants:

def prefix_route(route_function, prefix='', mask='{0}{1}'):
  '''
    Defines a new route function with a prefix.
    The mask argument is a `format string` formatted with, in that order:
      prefix, route
  '''
  def newroute(route, *args, **kwargs):
    '''New function to prefix the route'''
    return route_function(mask.format(prefix, route), *args, **kwargs)
  return newroute

On peut soutenir que ceci est quelque peu hackish et repose sur le fait que la fonction de route Flask nécessite a route comme premier argument de position.

Vous pouvez l'utiliser comme ceci:

app = Flask(__name__)
app.route = prefix_route(app.route, '/your_prefix')

NB: Cela ne vaut rien qu'il soit possible d'utiliser une variable dans le préfixe (par exemple en le définissant sur /<prefix>), puis de traiter ce préfixe dans les fonctions que vous décorez avec votre @app.route(...). Si vous le faites, vous devez évidemment déclarer le paramètre prefix dans vos fonctions décorées. De plus, vous pouvez vérifier le préfixe soumis avec certaines règles et renvoyer un 404 si la vérification échoue. Afin d'éviter une nouvelle implémentation 404 personnalisée, veuillez from werkzeug.exceptions import NotFound puis raise NotFound() si la vérification échoue.

7
7heo.tk

Donc, je pense qu’une réponse valable à cette question est: le préfixe doit être configuré dans l’application serveur réelle que vous utilisez lorsque le développement est terminé. Apache, nginx, etc.

Toutefois, si vous souhaitez que cela fonctionne pendant le développement lors de l'exécution de l'application Flask dans Debug, consultez this Gist .

Flask's DispatcherMiddleware à la rescousse!

Je vais copier le code ici pour la postérité:

"Serve a Flask app on a sub-url during localhost development."

from flask import Flask


APPLICATION_ROOT = '/spam'


app = Flask(__name__)
app.config.from_object(__name__)  # I think this adds APPLICATION_ROOT
                                  # to the config - I'm not exactly sure how!
# alternatively:
# app.config['APPLICATION_ROOT'] = APPLICATION_ROOT


@app.route('/')
def index():
    return 'Hello, world!'


if __== '__main__':
    # Relevant documents:
    # http://werkzeug.pocoo.org/docs/middlewares/
    # http://flask.pocoo.org/docs/patterns/appdispatch/
    from werkzeug.serving import run_simple
    from werkzeug.wsgi import DispatcherMiddleware
    app.config['DEBUG'] = True
    # Load a dummy app at the root URL to give 404 errors.
    # Serve app at APPLICATION_ROOT for localhost development.
    application = DispatcherMiddleware(Flask('dummy_app'), {
        app.config['APPLICATION_ROOT']: app,
    })
    run_simple('localhost', 5000, application, use_reloader=True)

Désormais, lors de l'exécution du code ci-dessus en tant qu'application Flask autonome, http://localhost:5000/spam/ affichera Hello, world!.

Dans un commentaire sur une autre réponse, j'ai indiqué que je souhaitais faire quelque chose comme ceci:

from flask import Flask, Blueprint

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
app.run()

# I now would like to be able to get to my route via this url:
# http://Host:8080/api/some_submodule/record/1/

Appliquer DispatcherMiddleware à mon exemple artificiel:

from flask import Flask, Blueprint
from flask.serving import run_simple
from flask.wsgi import DispatcherMiddleware

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
application = DispatcherMiddleware(Flask('dummy_app'), {
    app.config['APPLICATION_ROOT']: app
})
run_simple('localhost', 5000, application, use_reloader=True)

# Now, this url works!
# http://Host:8080/api/some_submodule/record/1/
3
Monkpit

Une autre manière complètement différente est d'utiliser mountpoints dans uwsgi.

Tiré de la doc sur Hébergement de plusieurs applications dans le même processus ( permalien ).

Dans votre uwsgi.ini vous ajoutez

[uwsgi]
mount = /foo=main.py
manage-script-name = true

# also stuff which is not relevant for this, but included for completeness sake:    
module = main
callable = app
socket = /tmp/uwsgi.sock

Si vous n'appelez pas votre fichier main.py, vous devez modifier les mount et module 

Votre main.py pourrait ressembler à ceci:

from flask import Flask, url_for
app = Flask(__name__)
@app.route('/bar')
def bar():
  return "The URL for this page is {}".format(url_for('bar'))
# end def

Et une configuration nginx (encore une fois pour compléter):

server {
  listen 80;
  server_name example.com

  location /foo {
    include uwsgi_params;
    uwsgi_pass unix:///temp/uwsgi.sock;
  }
}

À présent, l'appel de example.com/foo/bar affichera /foo/bar tel que renvoyé par la fonction url_for('bar') du flacon, car il s'adapte automatiquement. Ainsi, vos liens fonctionneront sans problèmes de préfixe.

2
luckydonald

J'avais besoin de ce qu'on appelle "context-root". Je l'ai fait dans le fichier conf sous /etc/httpd/conf.d/ en utilisant WSGIScriptAlias:

myapp.conf:

<VirtualHost *:80>
    WSGIScriptAlias /myapp /home/<myid>/myapp/wsgi.py

    <Directory /home/<myid>/myapp>
        Order deny,allow
        Allow from all
    </Directory>

</VirtualHost>

Alors maintenant, je peux accéder à mon application en tant que: http: // localhost: 5000/myapp

Voir le guide - http://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html

1
dganesh2002

Je préfère toujours utiliser les éléments suivants pour ajouter un préfixe à la totalité de la variable app:

app = Flask(__name__, root_path='/operators')

Clair et net.

1
Julian Camilleri

Ma solution dans laquelle les applications flask et PHP coexistent Nginx et PHP5.6

GARDER le flacon à la racine et PHP dans les sous-répertoires

Sudo vi /etc/php/5.6/fpm/php.iniAjouter 1 ligne Cgi.fix_pathinfo = 0

Sudo vi /etc/php/5.6/fpm/pool.d/www.conflisten = /run/php/php5.6-fpm.sock

uwsgi

Sudo vi /etc/nginx/sites-available/defaultUSE EMPLACEMENTS INSCRITS pour PHP et laissez FLASK rester à la racine

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # SSL configuration
    #
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server;
    #
    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

    root /var/www/html;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.php index.nginx-debian.html;

    server_name _;

    # Serve a static file (ex. favico) outside static dir.
    location = /favico.ico  {    
        root /var/www/html/favico.ico;    
    }

    # Proxying connections to application servers
    location / {
        include            uwsgi_params;
        uwsgi_pass         127.0.0.1:5000;
    }

    location /pcdp {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    location /phpmyadmin {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #   include snippets/fastcgi-php.conf;
    #
    #   # With php7.0-cgi alone:
    #   fastcgi_pass 127.0.0.1:9000;
    #   # With php7.0-fpm:
    #   fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #   deny all;
    #}
}

LIRE attentivement https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

Nous devons comprendre la correspondance de localisation (Aucun): Si aucun modificateur n’est présent, la localisation est interprétée comme une correspondance de préfixe. Cela signifie que l'emplacement indiqué sera mis en correspondance avec le début de l'URI de la demande pour déterminer une correspondance . =: Si un signe égal est utilisé, ce bloc sera considéré comme une correspondance si l'URI de la demande correspond exactement à l'emplacement indiqué. ~: Si un modificateur de tilde est présent, cet emplacement sera interprété comme une correspondance d'expressions régulières sensible à la casse. -insensitive expression régulière match . ^ ~: Si un modificateur carat et tilde est présent, et si ce bloc est sélectionné comme meilleure correspondance d'expression non régulière, la correspondance d'expression régulière n'aura pas lieu.

L'ordre est important, d'après la description de "l'emplacement" de nginx:

Pour trouver un emplacement correspondant à une requête donnée, nginx commence par vérifier les emplacements définis à l'aide des chaînes de préfixe (emplacements de préfixe). Parmi eux, l'emplacement avec le préfixe correspondant le plus long est sélectionné et mémorisé. Ensuite, les expressions régulières sont vérifiées, dans l'ordre d'apparition dans le fichier de configuration. La recherche d'expressions régulières se termine à la première correspondance et la configuration correspondante est utilisée. Si aucune correspondance avec une expression régulière n'est trouvée, la configuration de l'emplacement de préfixe précédemment mémorisé est utilisée.

Ça veut dire:

Premier =. ("correspondance du préfixe le plus long") Ensuite, celles implicites. ("correspondance du préfixe le plus long") Puis regex. (premier match)

1
Jayanta
from flask import Flask

app = Flask(__name__)

app.register_blueprint(bp, url_prefix='/abc/123')

if __== "__main__":
    app.run(debug='True', port=4444)


bp = Blueprint('burritos', __name__,
                        template_folder='templates')

@bp.route('/')
def test():
    return "success"
0
abhimanyu