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

Cada una de las técnicas y ejemplos siguientes da como resultado un objeto application que puede ejecutarse con cualquier servidor WSGI. Para desarrollo, utilice el comando flask run para iniciar un servidor de desarrollo. Para producción, véase Despliegue a 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.

Por ejemplo, podrías tener tu aplicación principal en / y tu interfaz backend en /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.

El nivel perfecto para la abstracción en ese sentido es la capa WSGI. Escribes tu propia aplicación WSGI que mira la petición que llega y la delega a tu aplicación Flask. Si esa aplicación no existe todavía, se crea dinámicamente y se recuerda.

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)

Este despachador se puede utilizar así:

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

El envío por una ruta en la URL es muy similar. En lugar de mirar la cabecera Host para averiguar el subdominio, simplemente se mira la ruta de la petición hasta la primera barra.

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

La gran diferencia entre éste y el de subdominio es que éste se devuelve a otra aplicación si la función creadora devuelve 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)