El Manejador es un concepto central en el framework Salvo, que puede entenderse como una unidad de procesamiento de solicitudes. Tiene dos usos principales:
Como Punto Final (Endpoint): Un objeto que implementa Handler
puede colocarse en el sistema de enrutamiento como punto final para procesar solicitudes. Cuando se usa la macro #[handler]
, una función puede actuar directamente como punto final; mientras que con la macro #[endpoint]
, no solo funciona como punto final, sino que también genera automáticamente documentación OpenAPI (esto se detallará más adelante).
Como Middleware: El mismo Handler
también puede usarse como middleware, para procesar solicitudes antes o después de que lleguen al punto final.
El flujo de procesamiento de solicitudes en Salvo puede verse como una "tubería": la solicitud primero pasa por una serie de middlewares (procesamiento vertical), luego llega al punto final coincidente (procesamiento horizontal). Tanto los middlewares como los puntos finales son implementaciones de Handler
, lo que brinda consistencia y flexibilidad al sistema.
La esencia del modelo de cebolla es que, mediante la posición de ctrl.call_next()
, se logra un flujo bidireccional de procesamiento de solicitudes y respuestas, permitiendo que cada middleware participe en el ciclo completo de solicitud-respuesta.
El Manejador es el objeto responsable de procesar las solicitudes Request. Es un Trait que contiene un método asíncrono handle
:
La función handle
tiene por defecto cuatro parámetros: &mut Request, &mut Depot, &mut Response, &mut FlowCtrl
. Depot es un almacenamiento temporal para datos relacionados con la solicitud actual.
Dependiendo de su uso, puede funcionar como middleware (hoop), que puede procesar la solicitud antes o después de que llegue al Handler
principal, como validación de inicio de sesión, compresión de datos, etc.
Los middlewares se añaden mediante la función hoop
de Router
. Los middlewares añadidos afectan al Router
actual y a todos sus Router
descendientes.
El Handler
también puede usarse como goal
, que participa en el enrutamiento y se ejecuta al final.
Handler
como Middleware (hoop)Cuando Handler
actúa como middleware, puede añadirse a tres tipos de objetos que admiten middlewares:
Service
: Todas las solicitudes pasan por los middlewares de Service
.
Router
: Solo cuando coincide la ruta, la solicitud pasa por los middlewares definidos en Service
y todos los middlewares recopilados en la ruta coincidente.
Catcher
: Cuando ocurre un error y no se ha escrito información de error personalizada, la solicitud pasa por los middlewares de Catcher
.
Handler
: El propio Handler
admite añadir middlewares para ejecutar lógica previa o posterior.
#[handler]
La macro #[handler]
simplifica enormemente la escritura de código y aumenta su flexibilidad.
Puede aplicarse a una función para que implemente Handler
:
Equivale a:
Como se ve, con #[handler]
el código es mucho más simple:
#[async_trait]
.Writer
o Scribe
pueden devolverse directamente. Aquí, &'static str
implementa Scribe
, por lo que puede devolverse directamente.#[handler]
no solo puede aplicarse a funciones, sino también a bloques impl
de struct
, haciendo que la struct
implemente Handler
. En este caso, la función handle
dentro del bloque impl
se reconoce como la implementación concreta de handle
en Handler
:
En Salvo, Handler
puede devolver Result
, siempre que Ok
y Err
implementen el trait Writer
. Dado que anyhow es bastante utilizado, al activar la función anyhow
, anyhow::Error
implementará Writer
. anyhow::Error
se mapea a InternalServerError
.
Para tipos de error personalizados, puedes mostrar diferentes páginas de error según sea necesario.