Dominar este arte
Por qué construir este framework
Como principiante, me costaba entender frameworks existentes como actix-web y Rocket. Al intentar reescribir un servicio web previo en Go usando Rust, cada framework parecía más complejo que los disponibles en Go. Dada la ya pronunciada curva de aprendizaje de Rust, ¿por qué complicar aún más los frameworks web?
Cuando Tokio presentó el framework Axum, me emocioné pensando que finalmente podría dejar de mantener mi propio framework web. Sin embargo, pronto me di cuenta de que aunque Axum parecía simple, requería extensas acrobacias con tipos y definiciones genéricas. Crear incluso un middleware básico exigía un profundo conocimiento de Rust y escribir grandes cantidades de código boilerplate oscuro.
Por lo tanto, decidí continuar manteniendo mi framework web único—uno que sea intuitivo, rico en características y amigable para principiantes.
¿Es Salvo adecuado para ti?
Salvo es simple pero completo y poderoso, posiblemente el más fuerte en el ecosistema de Rust. A pesar de sus capacidades, sigue siendo fácil de aprender y usar, evitándote cualquier dolor de "autocastración".
-
Ideal para principiantes en Rust: Las operaciones CRUD son comunes, y con Salvo, tales tareas se sienten tan directas como con frameworks en otros lenguajes (por ejemplo, Express, Koa, Gin, Flask). En algunos aspectos, Salvo es incluso más abstracto y conciso.
-
Adecuado para producción: Si tu objetivo es desplegar Rust en producción para servidores robustos y de alta velocidad, Salvo cumple con los requisitos. Aunque aún no está en la versión 1.0, sus características centrales han pasado por años de iteración, garantizando estabilidad y resolución oportuna de problemas.
-
Perfecto para ti, especialmente si tu cabello se está adelgazando y cayendo diariamente.
Logrando simplicidad
Hyper maneja muchas implementaciones de bajo nivel, convirtiéndolo en una base confiable para necesidades generales. Salvo sigue este enfoque, ofreciendo un sistema de enrutamiento poderoso y flexible junto con características esenciales como Acme, OpenAPI y autenticación JWT.
Salvo unifica Handlers y Middleware: el Middleware es un Handler. Ambos se adjuntan al Router a través de hoop. Esencialmente, procesan Request y pueden escribir datos en Response. Un Handler recibe tres parámetros: Request, Depot (para datos temporales durante el procesamiento de la solicitud) y Response.
Por conveniencia, puedes omitir parámetros innecesarios o ignorar su orden.
La API de enrutamiento es excepcionalmente simple pero poderosa. Para casos de uso típicos, solo necesitas enfocarte en el tipo Router.
Además, si una estructura implementa traits relevantes, Salvo puede generar automáticamente documentación OpenAPI, extraer parámetros, manejar errores con gracia y devolver mensajes amigables para el usuario. Esto hace que escribir handlers sea tan intuitivo como escribir funciones ordinarias. Exploraremos estas características en detalle más adelante. Aquí tienes un ejemplo:
En este ejemplo, JsonBody<CreateOrUpdateMessageLog> analiza automáticamente JSON del cuerpo de la solicitud en el tipo CreateOrUpdateMessageLog (soportando múltiples fuentes de datos y tipos anidados). La macro #[endpoint] genera documentación OpenAPI para este endpoint, simplificando la extracción de parámetros y el manejo de errores.
Sistema de enrutamiento
Creo que el sistema de enrutamiento de Salvo se destaca. Los routers pueden ser planos o en forma de árbol, distinguiendo entre árboles de lógica de negocio y árboles de directorio de acceso. El árbol de lógica de negocio organiza routers según necesidades comerciales, que pueden no alinearse con el árbol de directorio de acceso.
Típicamente, las rutas se escriben así:
A menudo, ver artículos y listarlos no requieren inicio de sesión del usuario, pero crear, editar o eliminar artículos sí. El sistema de enrutamiento anidado de Salvo maneja esto elegantemente. Podemos agrupar rutas públicas juntas:
Luego, agrupar rutas protegidas con middleware para autenticación:
Aunque ambos routers comparten el mismo path("articles"), pueden agregarse al mismo router padre, resultando en:
{id} coincide con un segmento de ruta. Típicamente, un id de artículo es numérico, por lo que podemos restringir la coincidencia con una expresión regular: r"{id:/\d+/}".