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"
La réponse dépend de la façon dont vous servez cette application.
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.
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)
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 uwsgi
gevent
, vous pouvez soit:
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).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')
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:
PATH_INFO
pour gérer l'URL préfixée.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.
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.
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 .
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/
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.
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:
<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
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.
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)
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"