Estructura y ciclo de vida de la aplicación

Flask hace que sea bastante fácil escribir una aplicación web. Pero hay bastantes partes diferentes en una aplicación y en cada petición que maneja. Saber lo que ocurre durante la configuración de la aplicación, el servicio y el manejo de las peticiones te ayudará a saber qué es posible en Flask y cómo estructurar tu aplicación.

Application Setup

El primer paso para crear una aplicación Flask es crear el objeto aplicación. Cada aplicación Flask es una instancia de la clase Flask, que recoge toda la configuración, extensiones y vistas.

from flask import Flask

app = Flask(__name__)
app.config.from_mapping(
    SECRET_KEY="dev",
)
app.config.from_prefixed_env()

@app.route("/")
def index():
    return "Hello, World!"

Esto se conoce como la «fase de configuración de la aplicación», es el código que escribes que está fuera de cualquier función de vista u otros manejadores. Puede estar dividido entre diferentes módulos y subpaquetes, pero todo el código que quieras que forme parte de tu aplicación debe ser importado para que pueda ser registrado.

Toda la configuración de la aplicación debe ser completada antes de empezar a servir su aplicación y manejar las peticiones. Esto se debe a que los servidores WSGI dividen el trabajo entre múltiples workers, o pueden ser distribuidos a través de múltiples máquinas. Si la configuración cambia en un trabajador, no hay forma de que Flask asegure la consistencia entre otros trabajadores.

Flask intenta ayudar a los desarrolladores a detectar algunos de estos problemas de ordenación de la configuración mostrando un error si se llaman métodos relacionados con la configuración después de gestionar las peticiones. En ese caso verás este error:

El método de configuración “route” ya no puede ser llamado en la aplicación. Ya ha gestionado su primera petición, cualquier cambio no se aplicará de forma consistente. Asegúrese de que todas las importaciones, decoradores, funciones, etc. necesarios para configurar la aplicación se realizan antes de ejecutarla.

Sin embargo, no es posible que Flask detecte todos los casos de configuración fuera de orden. En general, no hagas nada para modificar el objeto Flask app y los objetos Blueprint desde dentro de las funciones de vista que se ejecutan durante las peticiones. Esto incluye:

  • Añadir rutas, funciones de vista y otros gestores de peticiones con @app.route, @app.errorhandler, @app.before_request, etc.

  • Registro de blueprints.

  • Cargando configuración con app.config.

  • Configurar el entorno de plantillas Jinja con app.jinja_env.

  • Establecer una interfaz de sesión, en lugar de la cookie por defecto itsdangerous.

  • Establecer un proveedor JSON con app.json, en lugar del proveedor por defecto.

  • Creación e inicialización de extensiones Flask.

Servir la aplicación

Flask es un framework de aplicaciones WSGI. La otra mitad de WSGI es el servidor WSGI. Durante el desarrollo, Flask, a través de Werkzeug, proporciona un servidor WSGI de desarrollo con el comando CLI flask run. Cuando hayas terminado con el desarrollo, utiliza un servidor de producción para servir tu aplicación, ver Despliegue a producción.

Independientemente del servidor que utilices, seguirá la especificación PEP 3333 WSGI. Se le dirá al servidor WSGI cómo acceder a tu objeto de aplicación Flask, que es la aplicación WSGI. Entonces empezará a escuchar peticiones HTTP, traducirá los datos de la petición a un entorno WSGI, y llamará a la aplicación WSGI con esos datos. La aplicación WSGI devolverá los datos traducidos en una respuesta HTTP.

  1. El navegador u otro cliente realiza una petición HTTP.

  2. El servidor WSGI recibe la petición.

  3. El servidor WSGI convierte los datos HTTP en WSGI environ dict.

  4. El servidor WSGI llama a la aplicación WSGI con el environ.

  5. Flask, la aplicación WSGI, hace todo su procesamiento interno para enrutar la petición a una función de vista, manejar errores, etc.

  6. Flask traduce el retorno de la función View en datos de respuesta WSGI y los pasa al servidor WSGI.

  7. El servidor WSGI crea y envía una respuesta HTTP.

  8. El cliente recibe la respuesta HTTP.

Middleware

La aplicación WSGI anterior es un callable que se comporta de una determinada manera. Middleware es una aplicación WSGI que envuelve a otra aplicación WSGI. Es un concepto similar a los decoradores de Python. El middleware más externo será llamado por el servidor. Puede modificar los datos que se le pasan, luego llamar a la aplicación WSGI (o más middleware) que envuelve, y así sucesivamente. Y puede tomar el valor de retorno de esa llamada y modificarlo aún más.

Desde la perspectiva del servidor WSGI, hay una aplicación WSGI, la que llama directamente. Normalmente, Flask es la aplicación «real» al final de la cadena de middleware. Pero incluso Flask puede llamar a otras aplicaciones WSGI, aunque ese es un caso de uso avanzado y poco común.

Un middleware común que verás usado con Flask es ProxyFix de Werkzeug, que modifica la petición para que parezca que viene directamente de un cliente aunque haya pasado por proxies HTTP en el camino. Hay otros middleware que pueden manejar el servicio de archivos estáticos, autenticación, etc.

Cómo se gestiona una solicitud

Para nosotros, la parte interesante de los pasos anteriores es cuando Flask es llamado por el servidor WSGI (o middleware). En ese momento, va a hacer mucho para manejar la solicitud y generar la respuesta. En el más básico, que coincidirá con la URL a una función de vista, llame a la función de vista, y pasar el valor de retorno al servidor. Pero hay muchas más partes que puedes usar para personalizar su comportamiento.

  1. El servidor WSGI llama al objeto Flask, que llama a Flask.wsgi_app().

  2. Se crea un objeto RequestContext. Esto convierte el dict WSGI environ en un objeto Request. También crea un objeto AppContext.

  3. El contexto de aplicación es empujado, lo que hace que aplicación_actual y g estén disponibles.

  4. Se envía la señal appcontext_pushed.

  5. El contexto de petición es empujado, lo que hace que request y session estén disponibles.

  6. Se abre la sesión, cargando cualquier dato de sesión existente usando el session_interface de la aplicación, una instancia de SessionInterface.

  7. La URL se compara con las reglas de URL registradas con el decorador route() durante la configuración de la aplicación. Si no hay coincidencia, el error - normalmente un 404, 405, o redirección - se almacena para ser manejado más tarde.

  8. Se envía la señal request_started.

  9. Se llama a cualquier función decorada con url_value_preprocessor().

  10. Se llama a cualquier función decorada con before_request(). Si alguna de estas funciones devuelve un valor se trata como la respuesta inmediatamente.

  11. Si la URL no coincidía con una ruta hace unos pasos, ese error se produce ahora.

  12. Se llama a la función de vista decorada route() asociada a la URL coincidente y devuelve un valor que se utilizará como respuesta.

  13. Si en alguno de los pasos anteriores se ha producido una excepción y existe una función decorada con errorhandler() que coincida con la clase de excepción o el código de error HTTP, se llama a esta función para que gestione el error y devuelva una respuesta.

  14. Sea lo que sea lo que devuelva un valor de respuesta - una función anterior a la petición, la vista, o un manejador de errores, ese valor se convierte en un objeto Response.

  15. Cualquier función decorada con after_this_request() es llamada y luego borrada.

  16. Se llama a cualquier función decorada con after_request() que pueda modificar el objeto respuesta.

  17. La sesión se guarda, persistiendo cualquier dato de sesión modificado utilizando el session_interface de la aplicación.

  18. Se envía la señal request_finished.

  19. Si algún paso hasta el momento planteó una excepción, y no fue manejado por una función manejadora de errores, se maneja ahora. Las excepciones HTTP se tratan como respuestas con su código de estado correspondiente, otras excepciones se convierten en una respuesta genérica 500. Se envía la señal got_request_exception.

  20. El estado, las cabeceras y el cuerpo del objeto de respuesta se devuelven al servidor WSGI.

  21. Se llama a cualquier función decorada con teardown_request().

  22. Se envía la señal request_tearing_down.

  23. El contexto de petición ha sido eliminado, request y session ya no están disponibles.

  24. Se llama a cualquier función decorada con teardown_appcontext().

  25. Se envía la señal appcontext_tearing_down.

  26. El contexto de la aplicación ha desaparecido, current_app y g ya no están disponibles.

  27. Se envía la señal appcontext_popped.

Hay incluso más decoradores y puntos de personalización que estos, pero que no forman parte de todos los ciclos de vida de las peticiones. Son más específicos para ciertas cosas que podrías usar durante una petición, como plantillas, construcción de URLs, o manejo de datos JSON. Consulta el resto de esta documentación, así como API para profundizar en el tema.