web-dev-qa-db-de.com

Fügen Sie allen Flask-Routen ein Präfix hinzu

Ich habe ein Präfix, das ich jeder Route hinzufügen möchte. Im Moment füge ich der Route bei jeder Definition eine Konstante hinzu. Gibt es eine Möglichkeit, dies automatisch zu tun?

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

Die Antwort hängt davon ab, wie Sie diese Anwendung bereitstellen.

Submontiert in einem anderen WSGI-Container

Angenommen, Sie führen diese Anwendung in einem WSGI-Container aus (mod_wsgi, uwsgi, gunicorn usw.); Sie müssen tatsächlich mount an diesem Präfix der Anwendung als Unterabschnitt dieses WSGI-Containers (alles, was WSGI spricht, tun) und Ihren APPLICATION_ROOT config-Wert auf Ihr Präfix setzen:

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/"

Wenn Sie den Wert APPLICATION_ROOT config festlegen, beschränken Sie einfach das Sitzungscookie von Flask auf dieses URL-Präfix. Alles andere wird automatisch von Flask und den hervorragenden WSGI-Handhabungsfunktionen von Werkzeug bearbeitet.

Ein Beispiel für das ordnungsgemäße Anhängen Ihrer App

Wenn Sie sich nicht sicher sind, was der erste Absatz bedeutet, werfen Sie einen Blick auf diese Beispielanwendung, in der Flask installiert ist:

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)

Anfragen an die App weiterleiten

Wenn Sie andererseits Ihre Flask-Anwendung im Stammverzeichnis des WSGI-Containers ausführen und Anforderungen an sie weiterleiten (z. B. wenn es sich um eine FastCGI handelt, oder wenn nginx proxy_pass-Anforderungen für ein Unterprogramm ist). Endpunkt zu Ihrem Standalone-Server uwsgigevent, dann können Sie entweder:

  • Verwenden Sie einen Blueprint, wie Miguel in seiner Antwort zeigt.
  • _/oder verwenden Sie die DispatcherMiddleware von werkzeug (oder die PrefixMiddleware von su27s Antwort ), um Ihre Anwendung auf dem eigenständigen WSGI-Server, den Sie verwenden, bereitzustellen. (Siehe Ein Beispiel für das ordnungsgemäße Submounten Ihrer App für den zu verwendenden Code).
59
Sean Vieira

Sie können Ihre Routen in einen Bauplan setzen:

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"

Dann registrieren Sie den Blueprint mit einem Präfix bei der Anwendung:

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

Sie sollten beachten, dass der APPLICATION_ROOT NICHT für diesen Zweck ist.

Sie müssen lediglich eine Middleware schreiben, um folgende Änderungen vorzunehmen:

  1. Ändern Sie PATH_INFO, um die vorangestellte URL zu behandeln.
  2. SCRIPT_NAME ändern, um die vorangestellte URL zu generieren.

So was:

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

Wickeln Sie Ihre App mit der Middleware wie folgt:

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)

Besuchen Sie http://localhost:9010/foo/bar,

Sie erhalten das richtige Ergebnis: The URL for this page is /foo/bar

Vergessen Sie nicht, die Cookie-Domain festzulegen, wenn Sie dies benötigen.

Diese Lösung wird von Larivact's Gist gegeben. Der APPLICATION_ROOT ist nicht für diesen Job, obwohl es so aussieht. Es ist wirklich verwirrend.

27
su27

Dies ist eher eine Python-Antwort als eine Flask/Werkzeug-Antwort. aber es ist einfach und funktioniert.

Wenn Sie wie ich möchten, dass Ihre Anwendungseinstellungen (aus einer .ini-Datei geladen) auch das Präfix Ihrer Flask-Anwendung enthalten (damit der Wert nicht während der Bereitstellung, sondern zur Laufzeit festgelegt wird), können Sie sich für Folgendes entscheiden:

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

Dies ist wohl etwas hässlich und beruht auf der Tatsache, dass die Flask-Routenfunktion als erstes Positionsargument eine route erfordert.

Sie können es so verwenden:

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

NB: Es ist nichts wert, dass es möglich ist, eine Variable im Präfix zu verwenden (z. B. indem Sie sie auf /<prefix> setzen) und dieses Präfix dann in den Funktionen zu verarbeiten, die Sie mit Ihrer @app.route(...) verzieren. Wenn Sie dies tun, müssen Sie natürlich den Parameter prefix in Ihren dekorierten Funktionen angeben. Darüber hinaus möchten Sie möglicherweise das gesendete Präfix anhand einiger Regeln überprüfen und einen 404 zurückgeben, wenn die Prüfung fehlschlägt. Um eine benutzerdefinierte 404-Neuimplementierung zu vermeiden, from werkzeug.exceptions import NotFound und dann raise NotFound(), falls die Prüfung fehlschlägt.

7
7heo.tk

Ich glaube also, dass eine gültige Antwort darauf lautet: Das Präfix sollte in der tatsächlichen Serveranwendung konfiguriert sein, die Sie verwenden, wenn die Entwicklung abgeschlossen ist. Apache, Nginx usw.

Wenn Sie möchten, dass dies während der Entwicklung funktioniert, während Sie die Flask-App im Debug-Modus ausführen, sehen Sie sich this Gist an.

Flaskes DispatcherMiddleware zur Rettung!

Ich werde den Code hier für die Nachwelt kopieren:

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

Wenn Sie den obigen Code nun als eigenständige Flask-App ausführen, zeigt http://localhost:5000/spam/Hello, world! an.

In einem Kommentar zu einer anderen Antwort brachte ich zum Ausdruck, dass ich so etwas tun möchte:

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/

Anwenden von DispatcherMiddleware auf mein erfundenes Beispiel:

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

Ein anderer völlig anderer Weg ist mit mountpoints in uwsgi.

Aus dem Dokument über Hosting mehrerer Apps im selben Prozess ( permalink ).

In Ihrem uwsgi.ini fügen Sie hinzu

[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

Wenn Sie Ihre Datei nicht main.py aufrufen, müssen Sie sowohl die mount als auch die module ändern. 

Ihr main.py könnte so aussehen:

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

Und eine Nginx-Konfig (wieder zur Vollständigkeit):

server {
  listen 80;
  server_name example.com

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

Beim Aufruf von example.com/foo/bar wird /foo/bar so angezeigt, wie es von flask url_for('bar') zurückgegeben wird, da es sich automatisch anpasst. Auf diese Weise funktionieren Ihre Links ohne Präfixprobleme.

2
luckydonald

Ich brauchte ein ähnliches sogenanntes "Kontextstammverzeichnis". Ich habe es in der conf-Datei unter /etc/httpd/conf.d/ mit WSGIScriptAlias ​​gemacht:

myapp.conf:

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

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

</VirtualHost>

So kann ich jetzt auf meine App zugreifen: http: // localhost: 5000/myapp

Siehe Handbuch - http://modwsgi.readthedocs.io/de/develop/user-guides/quick-configuration-guide.html

1
dganesh2002

Ich bevorzuge immer die folgende, wenn es darum geht, ein Präfix für die gesamte app hinzuzufügen:

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

Sauber und klar.

1

Meine Lösung, bei der Flaschen und PHP - Apps nebeneinander existieren Nginx und PHP5.6

KEEP Flask in root und PHP in Unterverzeichnissen

Sudo vi /etc/php/5.6/fpm/php.iniAdd 1 Zeile 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 NESTED LOCATIONS für PHP und lassen Sie FLASK im Stammverzeichnis bleiben

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;
    #}
}

LESEN Sie sorgfältig https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

Wir müssen den Ortsabgleich verstehen. __ (keiner): Wenn keine Modifikatoren vorhanden sind, wird der Ort als Präfixübereinstimmung interpretiert. Dies bedeutet, dass der angegebene Ort mit dem Beginn des Anforderungs-URIs verglichen wird, um eine Übereinstimmung zu ermitteln =: Wenn ein Gleichheitszeichen verwendet wird, wird dieser Block als Übereinstimmung betrachtet, wenn der Anforderungs-URI genau mit dem angegebenen Ort übereinstimmt. ~: Wenn ein Tilde-Modifikator vorhanden ist, wird diese Position als Übereinstimmung zwischen Groß- und Kleinschreibung eines regulären Ausdrucks interpretiert ~ *: Wenn ein Tilde- und Sternchen-Modifikator verwendet wird, wird der Standortblock als Fall interpretiert -insensitive reguläre Ausdrucksübereinstimmung . ^ ~: Wenn ein carat- und tilde-Modifikator vorhanden ist und dieser Block als bester nicht-regulärer Ausdrucksabgleich ausgewählt wird, findet kein regulärer Ausdrucksabgleich statt.

Die Reihenfolge ist aus der Beschreibung von nginx "Standort" wichtig:

Um eine Position zu finden, die einer bestimmten Anforderung entspricht, prüft nginx zunächst die mit den Präfixzeichenfolgen (Präfixpositionen) definierten Positionen. Unter ihnen wird der Ort mit dem längsten übereinstimmenden Präfix ausgewählt und gespeichert. Dann werden reguläre Ausdrücke in der Reihenfolge ihres Aussehens in der Konfigurationsdatei geprüft. Die Suche nach regulären Ausdrücken endet mit der ersten Übereinstimmung und die entsprechende Konfiguration wird verwendet. Wenn keine Übereinstimmung mit einem regulären Ausdruck gefunden wird, wird die zuvor gespeicherte Konfiguration der Präfixposition verwendet.

Es bedeutet:

Erstes =. ("längst passendes Präfix") Dann implizite. (Übereinstimmung mit "längst passendem Präfix") Dann Regex. (erstes Spiel)

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