Handler
Visión General Rápida
Handler es un concepto central en el framework Salvo, que puede entenderse simplemente como una unidad de procesamiento de solicitudes. Tiene dos propósitos principales:
-
Como Punto Final (Endpoint): Un objeto que implementa
Handlerpuede colocarse en el sistema de enrutamiento como el punto final para procesar solicitudes. Al usar la macro#[handler], una función puede usarse directamente como un endpoint; mientras que usar la macro#[endpoint]no solo permite que sirva como endpoint, sino que también genera automáticamente documentación OpenAPI (esto se detallará en documentación posterior). -
Como Middleware: El mismo
Handlertambién puede usarse como middleware para procesar solicitudes antes o después de que lleguen al endpoint final.
El flujo de procesamiento de solicitudes de Salvo puede verse como una "tubería" (pipeline): una solicitud primero pasa por una serie de middleware (procesamiento vertical) y luego llega al endpoint coincidente (procesamiento horizontal). Tanto el middleware como los endpoints son implementaciones de Handler, lo que garantiza consistencia y flexibilidad en todo el sistema.
Diagrama de Flujo del Handler en Salvo
Middleware y el Modelo de Cebolla
La esencia del modelo de cebolla es que al colocar ctrl.call_next() antes y después de una lógica específica, se implementa un flujo de procesamiento bidireccional para solicitudes y respuestas, permitiendo que cada middleware participe en el ciclo completo de solicitud-respuesta.
Ejemplo de Estructura Completa de Middleware
¿Qué es un Handler?
Un Handler es el objeto concreto responsable de procesar objetos Request. Handler en sí es un Trait que contiene un método asíncrono handle:
La firma predeterminada de la función handle incluye cuatro parámetros, en orden: &mut Request, &mut Depot, &mut Response, &mut FlowCtrl. Depot es un almacenamiento temporal que puede contener datos relacionados con la solicitud actual.
Dependiendo de cómo se use, puede servir como middleware (aro o hoop), que puede realizar procesamiento antes o después de que la solicitud llegue al Handler formal de procesamiento de solicitudes, como por ejemplo: verificación de inicio de sesión, compresión de datos, etc.
El middleware se agrega mediante la función hoop de un Router. El middleware agregado afecta al Router actual y a todos sus Routers descendientes.
Un Handler también puede usarse como un Handler que participa en la coincidencia de rutas y se ejecuta finalmente, conocido como un objetivo (goal).
Handler como Middleware (hoop)
Cuando un Handler actúa como middleware, puede agregarse a los siguientes tres tipos de objetos que admiten middleware:
Service: Cualquier solicitud pasará por el middleware en elService.Router: Solo cuando la coincidencia de ruta tenga éxito, la solicitud pasará por el middleware definido en elServicey todo el middleware recopilado a lo largo de la ruta coincidente.Catcher: Cuando ocurre un error y no se ha escrito información de error personalizada, la solicitud pasará por el middleware en elCatcher.Handler: ElHandleren sí mismo admite agregar envoltorios de middleware para ejecutar alguna lógica previa o posterior.
Uso de la Macro #[handler]
La macro #[handler] puede simplificar enormemente la escritura de código y mejorar su flexibilidad.
Puede aplicarse a una función para hacer que implemente Handler:
Esto es equivalente a:
Como puedes ver, con #[handler], el código se vuelve mucho más simple:
- No es necesario agregar manualmente
#[async_trait]. - Se omiten parámetros innecesarios en la función, y los parámetros requeridos pueden organizarse en cualquier orden.
- Para objetos que implementan las abstracciones
WriteroScribe, pueden devolverse directamente como valor de retorno de la función. Aquí,&'static strimplementaScribe, por lo que puede devolverse directamente.
#[handler] no solo puede aplicarse a funciones, sino también al bloque impl de un struct para hacer que el struct implemente Handler. En este caso, la función handle dentro del bloque impl se reconoce como la implementación concreta del método handle en Handler:
Manejo de Errores
En Salvo, un Handler puede devolver un Result, siempre que tanto el tipo Ok como el Err dentro del Result implementen el trait Writer.
Considerando el uso generalizado de anyhow, cuando la característica anyhow está habilitada, anyhow::Error implementará el trait Writer. anyhow::Error se mapeará a InternalServerError.
Para tipos de error personalizados, puedes generar diferentes páginas de error según sea necesario.