137 votes

Ajouter un préfixe à toutes les routes Flask

J'ai un préfixe que je veux ajouter à chaque route. En ce moment, j'ajoute une constante à la route à chaque définition. Y a-t-il un moyen de le faire automatiquement ?

PREFIX = "/abc/123"

@app.route(PREFIX + "/")
def index_page():
  return "Ceci est un site web à propos de burritos"

@app.route(PREFIX + "/about")
def about_page():
  return "Ceci est un site web à propos de burritos"

141voto

Miguel Points 15459

Vous pouvez mettre vos routes dans un blueprint :

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

@bp.route("/")
def index_page():
  return "Ceci est un site web sur les burritos"

@bp.route("/about")
def about_page():
  return "Ceci est un site web sur les burritos"

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

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

97voto

Sean Vieira Points 47080

La réponse dépend de la manière dont vous servez cette application.

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

En supposant que vous allez exécuter cette application à l'intérieur d'un conteneur WSGI (mod_wsgi, uwsgi, gunicorn, etc); vous devez en fait monter, à ce préfixe l'application en tant que sous-partie de ce conteneur WSGI (n'importe quoi qui parle WSGI fera l'affaire) et définir votre APPLICATION_ROOT la valeur de configuration à votre préfixe:

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

@app.route("/")
def index():
    return "L'URL de cette page est {}".format(url_for("index"))

# Renvoie "L'URL de cette page est /abc/123/"

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

Un exemple de sous-montage approprié de votre application

Si vous n'êtes pas sûr de ce que signifie le premier paragraphe, jetez un œil à cet exemple d'application avec Flask montée à l'intérieur :

from flask import Flask, url_for
from werkzeug.serving import run_simple
from werkzeug.middleware.dispatcher import DispatcherMiddleware

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

@app.route('/')
def index():
    return 'L URL de cette page est {}'.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 __name__ == '__main__':
    app.run('localhost', 5000)

Transmettre les demandes à l'application

Si, d'autre part, vous exécutez votre application Flask à la racine de son conteneur WSGI et transmettez les demandes à celui-ci (par exemple, si elle est FastCGI, ou si nginx est en train de transmettre les demandes pour un sous-point d'accès à votre serveur uwsgi / gevent autonome alors vous pouvez soit:

  • Utiliser un Blueprint, comme le souligne Miguel dans sa réponse.
  • ou utiliser le DispatcherMiddleware de werkzeug (ou le PrefixMiddleware de la réponse de su27) pour monter votre application dans le serveur WSGI autonome que vous utilisez. (Voir Un exemple de sous-montage approprié de votre application ci-dessus pour le code à utiliser).

74voto

su27 Points 945

Vous devez noter que APPLICATION_ROOT n'est pas à cette fin.

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

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

Comme ceci :

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 ["Cette URL ne fait pas partie de l'application.".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 "L'URL de cette page est {}".format(url_for('bar'))

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

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

Vous obtiendrez le bon résultat : L'URL de cette page est /foo/bar

Et n'oubliez pas de définir le domaine du cookie si nécessaire.

Cette solution est fournie par le gist de Larivact. Le APPLICATION_ROOT n'est pas fait pour cela, bien qu'il semble l'être. C'est vraiment déroutant.

13voto

7heo.tk Points 996

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

Si, comme moi, vous souhaitez que les paramètres de votre application (chargés à partir d'un fichier .ini) contiennent également le préfix de votre application Flask (ainsi, de ne pas avoir la valeur définie pendant le déploiement, mais pendant l'exécution), vous pouvez opter pour ce qui suit :

def prefix_route(route_function, prefix='', mask='{0}{1}'):
  '''
    Définit une nouvelle fonction de route avec un préfix.
    L'argument mask est une `chaine de formatage` formatée avec, dans cet ordre :
      préfix, route
  '''
  def newroute(route, *args, **kwargs):
    '''Nouvelle fonction pour ajouter le préfix à la route'''
    return route_function(mask.format(prefix, route), *args, **kwargs)
  return newroute

Arguablement, c'est un peu hackish et repose sur le fait que la fonction de route Flask requiert une route en premier argument positionnel.

Vous pouvez l'utiliser comme ceci :

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

NB : Il est à noter qu'il est possible d'utiliser une variable dans le préfix (par exemple en le définissant comme /), puis de traiter ce préfix dans les fonctions que vous décorez avec votre @app.route(...). Si vous le faites, vous devez évidemment déclarer le paramètre préfix dans votre/dans vos fonction(s) décorée(s). De plus, vous pouvez vouloir vérifier le préfix soumis par rapport à certaines règles, et renvoyer un 404 si la vérification échoue. Afin d'éviter une ré-implémentation personnalisée du 404, veuillez from werkzeug.exceptions import NotFound et ensuite raise NotFound() si la vérification échoue.

5voto

Monkpit Points 756

Donc, je crois qu'une réponse valable à cela est : le préfixe doit être configuré dans l'application serveur réelle que vous utilisez une fois le développement terminé. Apache, nginx, etc.

Cependant, si vous souhaitez que cela fonctionne pendant le développement tout en exécutant l'application Flask en mode débogage, jetez un coup d'œil à ce gist.

Le DispatcherMiddleware de Flask à la rescousse!

Je copierai le code ici pour mémoire:

"Servir une application Flask sur une sous-URL pendant le développement en local."

from flask import Flask

APPLICATION_ROOT = '/spam'

app = Flask(__name__)
app.config.from_object(__name__)  # Je pense que cela ajoute APPLICATION_ROOT
                                  # à la configuration - Je ne suis pas exactement sûr comment!
# ou bien:
# app.config['APPLICATION_ROOT'] = APPLICATION_ROOT

@app.route('/')
def index():
    return 'Bonjour, monde!'

if __name__ == '__main__':
    # Documents pertinents:
    # 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
    # Charger une application bidon à l'URL racine pour générer des erreurs 404.
    # Servir l'application à APPLICATION_ROOT pour le développement en local.
    application = DispatcherMiddleware(Flask('dummy_app'), {
        app.config['APPLICATION_ROOT']: app,
    })
    run_simple('localhost', 5000, application, use_reloader=True)

Maintenant, en exécutant le code ci-dessus en tant qu'application Flask autonome, http://localhost:5000/spam/ affichera Bonjour, monde!.

Dans un commentaire sur une autre réponse, j'ai exprimé le souhait de faire quelque chose comme ceci:

from flask import Flask, Blueprint

# Imaginons que module_blueprint définit une route, '/record//'
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()

# Je voudrais maintenant pouvoir accéder à ma route via cette URL:
# http://host:8080/api/some_submodule/record/1/

Application du DispatcherMiddleware à mon exemple inventé:

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

# Imaginons que module_blueprint définit une route, '/record//'
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)

# Maintenant, cette URL fonctionne!
# http://host:8080/api/some_submodule/record/1/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X