Añadir un prefijo a todas las rutas del frasco


Tengo un prefijo que quiero añadir a cada ruta. Ahora mismo agrego una constante a la ruta en cada definición. ¿Hay alguna manera de hacer esto automáticamente?

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"
Author: davidism, 2013-09-23

8 answers

La respuesta depende de cómo está sirviendo esta solicitud.

Sub-montado dentro de otro contenedor WSGI

Suponiendo que va a ejecutar esta aplicación dentro de un contenedor WSGI (mod_wsgi, uwsgi, gunicorn, etc); necesita realmente montar, en ese prefijo la aplicación como una sub-parte de ese contenedor WSGI (cualquier cosa que hable WSGI hará) y establecer su APPLICATION_ROOT valor de configuración para su prefijo:

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

Estableciendo el APPLICATION_ROOT el valor de configuración simplemente limita la cookie de sesión de Flask a ese prefijo de URL. Todo lo demás se manejará automáticamente por ti mediante las excelentes capacidades de manejo WSGI de Flask y Werkzeug.

Un ejemplo de sub-montar correctamente su aplicación

Si no está seguro de lo que significa el primer párrafo, eche un vistazo a esta aplicación de ejemplo con Matraz montado dentro de ella:

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 __name__ == '__main__':
    app.run('localhost', 5000)

Proxy de solicitudes a la aplicación

Si, por otro lado, usted será ejecutar su aplicación Flask en la raíz de su contenedor WSGI y proxy de solicitudes a ella (por ejemplo, si está siendo FASTCGI'd a, o si nginx es proxy_pass-ing solicitudes para un sub-endpoint a su stand-alone uwsgi / gevent servidor entonces usted puede:

  • Usa un Plano, como Miguel señala en su respuesta.
  • o use el DispatcherMiddleware de werkzeug (o el PrefixMiddleware de la respuesta de su27) para sub-montar su aplicación en el servidor WSGI independiente estás consumiendo. (Ver Un ejemplo de sub-montar correctamente su aplicación anterior para el código a utilizar).
 57
Author: Sean Vieira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:51

Puedes poner tus rutas en un plano:

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"

Luego registra el blueprint con la aplicación usando un prefijo:

app = Flask(__name__)
app.register_blueprint(bp, url_prefix='/abc/123')
 64
Author: Miguel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-23 21:24:12

Debe tener en cuenta que el APPLICATION_ROOT NO es para este propósito.

Todo lo que tienes que hacer es escribir un middleware para hacer los siguientes cambios:

  1. modifica PATH_INFO para manejar la url prefijada.
  2. modifica SCRIPT_NAME para generar la url prefijada.

Así:

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

Envuelve tu app con el middleware, así:

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 __name__ == '__main__':
    app.run('0.0.0.0', 9010)

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

Obtendrá el resultado correcto: The URL for this page is /foo/bar

Y no olvide configurar el dominio de cookies si necesito hacerlo.

Esta solución viene dada por la esencia de Larivact. El APPLICATION_ROOT no es para este trabajo, aunque parece ser. Es muy confuso.

 25
Author: su27,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-14 06:55:33

Esta es más una respuesta python que una respuesta Flask/werkzeug; pero es simple y funciona.

Si, como yo, desea que la configuración de su aplicación (cargada desde un archivo .ini) también contenga el prefijo de su aplicación Flask (por lo tanto, no debe tener el valor establecido durante la implementación, sino durante el tiempo de ejecución), puede optar por lo siguiente:

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

Podría decirse que esto es algo hackish y se basa en el hecho de que la función de ruta del matraz requiere a route como primera posicional argumento.

Puedes usarlo así:

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

NB: No vale nada que sea posible usar una variable en el prefijo (por ejemplo configurándolo a /<prefix>), y luego procesar este prefijo en las funciones que decores con tu @app.route(...). Si lo hace, obviamente tiene que declarar el parámetro prefix en su(s) función (es) decorada (s). Además, es posible que desee comprobar el prefijo enviado contra algunas reglas, y devolver un 404 si la comprobación falla. Para evitar un 404 re-implementación personalizada, por favor from werkzeug.exceptions import NotFound y luego raise NotFound() si la comprobación falla.

 6
Author: 7heo.tk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-10 16:28:14

Por lo tanto, creo que una respuesta válida a esto es: el prefijo debe configurarse en la aplicación de servidor real que use cuando se complete el desarrollo. Apache, nginx, etc.

Sin embargo, si desea que esto funcione durante el desarrollo mientras ejecuta la aplicación Flask en debug, eche un vistazo a esta síntesis.

Frasco de DispatcherMiddleware al rescate!

Voy a copiar el código aquí para la posteridad:

"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 __name__ == '__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)

Ahora, al ejecutar el código anterior como una aplicación Flask independiente, http://localhost:5000/spam/ mostrará Hello, world!.

En un comentario sobre otra respuesta, expresé que deseaba hacer algo como esto:

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/

Aplicando DispatcherMiddleware a mi ejemplo artificial:

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
Author: Monkpit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-18 19:52:07

Necesitaba la llamada "raíz de contexto"similar. Lo hice en el fichero conf bajo /etc/httpd / conf.d / usando WSGIScriptAlias:

Myapp.conf:

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

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

</VirtualHost>

Así que ahora puedo acceder a mi aplicación como : http://localhost:5000/myapp

Ver la guía - http://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html

 1
Author: dganesh2002,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-11 18:20:38

Otra forma completamente diferente es con puntos de montaje{[14] {} en[3]}.

Del documento acerca de El alojamiento de varias aplicaciones en el mismo proceso (permalink ).

En su uwsgi.ini usted agrega

[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 no llamas a tu archivo main.py, necesitas cambiar tanto el mount como el module

Su main.py podría verse así: {[15]]}

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

Y una configuración de nginx (de nuevo para completar):

server {
  listen 80;
  server_name example.com

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

Ahora llamando example.com/foo/bar se mostrará /foo/bar como devuelto por el matraz url_for('bar'), ya que se adapta automáticamente. De esa manera tus enlaces funcionarán sin problemas de prefijo.

 1
Author: luckydonald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-07 10:13:53

Siempre prefiero usar lo siguiente cuando se trata de agregar un prefijo en todo app:

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

Limpio y claro.

 1
Author: Julian Camilleri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-07 10:39:17