Despacho de aplicaciones

El despacho de aplicaciones es el proceso de combinar múltiples aplicaciones Flask en el nivel WSGI. Puedes combinar no sólo aplicaciones Flask sino cualquier aplicación WSGI. Esto te permitiría ejecutar una aplicación Django y una aplicación Flask en el mismo intérprete una al lado de la otra si lo deseas. La utilidad de esto depende de cómo funcionen las aplicaciones internamente.

La diferencia fundamental con Grandes aplicaciones como paquetes es que en este caso estás ejecutando la misma o diferentes aplicaciones Flask que están completamente aisladas entre sí. Ejecutan configuraciones diferentes y se despachan en el nivel WSGI.

Trabajar con este documento

Each of the techniques and examples below results in an application object that can be run with any WSGI server. For development, use the flask run command to start a development server. For production, see Despliegue en producción.

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

Combinación de aplicaciones

Si tienes aplicaciones completamente separadas y quieres que funcionen una al lado de la otra en el mismo proceso de interpretación de Python puedes aprovechar la werkzeug.wsgi.DispatcherMiddleware. La idea aquí es que cada aplicación Flask es una aplicación WSGI válida y son combinadas por el middleware despachador en una más grande que es despachada basada en el prefijo.

For example you could have your main application run on / and your backend interface on /backend.

from werkzeug.middleware.dispatcher import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend': backend
})

Despacho por subdominio

A veces puedes querer usar múltiples instancias de la misma aplicación con diferentes configuraciones. Asumiendo que la aplicación es creada dentro de una función y que puedes llamar a esa función para instanciarla, eso es realmente fácil de implementar. Para desarrollar tu aplicación de forma que soporte la creación de nuevas instancias en funciones echa un vistazo al patrón Fábricas de aplicaciones.

Un ejemplo muy común sería crear aplicaciones por subdominio. Por ejemplo, configuras tu servidor web para que envíe todas las peticiones de todos los subdominios a tu aplicación y luego utilizas la información del subdominio para crear instancias específicas para cada usuario. Una vez que tengas tu servidor configurado para escuchar en todos los subdominios puedes usar una aplicación WSGI muy simple para hacer la creación de aplicaciones dinámicas.

The perfect level for abstraction in that regard is the WSGI layer. You write your own WSGI application that looks at the request that comes and delegates it to your Flask application. If that application does not exist yet, it is dynamically created and remembered.

from threading import Lock

class SubdomainDispatcher:

    def __init__(self, domain, create_app):
        self.domain = domain
        self.create_app = create_app
        self.lock = Lock()
        self.instances = {}

    def get_application(self, host):
        host = host.split(':')[0]
        assert host.endswith(self.domain), 'Configuration error'
        subdomain = host[:-len(self.domain)].rstrip('.')
        with self.lock:
            app = self.instances.get(subdomain)
            if app is None:
                app = self.create_app(subdomain)
                self.instances[subdomain] = app
            return app

    def __call__(self, environ, start_response):
        app = self.get_application(environ['HTTP_HOST'])
        return app(environ, start_response)

This dispatcher can then be used like this:

from myapplication import create_app, get_user_for_subdomain
from werkzeug.exceptions import NotFound

def make_app(subdomain):
    user = get_user_for_subdomain(subdomain)
    if user is None:
        # if there is no user for that subdomain we still have
        # to return a WSGI application that handles that request.
        # We can then just return the NotFound() exception as
        # application which will render a default 404 page.
        # You might also redirect the user to the main page then
        return NotFound()

    # otherwise create the application for the specific user
    return create_app(user)

application = SubdomainDispatcher('example.com', make_app)

Despacho por Ruta

Dispatching by a path on the URL is very similar. Instead of looking at the Host header to figure out the subdomain one simply looks at the request path up to the first slash.

from threading import Lock
from wsgiref.util import shift_path_info

class PathDispatcher:

    def __init__(self, default_app, create_app):
        self.default_app = default_app
        self.create_app = create_app
        self.lock = Lock()
        self.instances = {}

    def get_application(self, prefix):
        with self.lock:
            app = self.instances.get(prefix)
            if app is None:
                app = self.create_app(prefix)
                if app is not None:
                    self.instances[prefix] = app
            return app

    def __call__(self, environ, start_response):
        app = self.get_application(_peek_path_info(environ))
        if app is not None:
            shift_path_info(environ)
        else:
            app = self.default_app
        return app(environ, start_response)

def _peek_path_info(environ):
    segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
    if segments:
        return segments[0]

    return None

The big difference between this and the subdomain one is that this one falls back to another application if the creator function returns None.

from myapplication import create_app, default_app, get_user_for_prefix

def make_app(prefix):
    user = get_user_for_prefix(prefix)
    if user is not None:
        return create_app(user)

application = PathDispatcher(default_app, make_app)