Estado Afijo: Datos Compartidos en Solicitudes

El middleware Estado Afijo se utiliza para agregar datos compartidos al Depot. Para utilizar la funcionalidad Estado Afijo, es necesario habilitar la característica affix-state en Cargo.toml.

Análisis de la Característica

Estado Afijo proporciona una forma sencilla de compartir datos durante el procesamiento de solicitudes. Permite:

  • Inyectar configuraciones globales o datos compartidos durante la configuración de rutas
  • Acceder a estos datos a través del Depot en cualquier manejador
  • Soportar cualquier tipo clonable como datos de estado

Comparación con Otros Frameworks para una Comprensión Rápida del Concepto

FrameworkLenguajeEnfoque de Gestión de Estado
Salvo (Estado Afijo)RustAlmacena y accede a través del Depot, soporta múltiples tipos
AxumRustAlmacena estado mediante Extension, similar pero con uso diferente
Actix-webRustUtiliza App Data y Web::Data para compartir estado
GinGoUsa context.Set y context.Get para almacenar y recuperar datos
EchoGoUtiliza context.Set y context.Get para gestionar estado compartido
SpringJavaEmplea ApplicationContext o anotaciones @Bean para gestión de dependencias
QuarkusJavaUsa mecanismos CDI e inyección de dependencias
Express.jsJavaScriptUtiliza app.locals o req.app.locals para almacenar estado global
Nest.jsJavaScriptEmplea un sistema de inyección de dependencias para gestionar servicios compartidos
Koa.jsJavaScriptUsa ctx.state para almacenar estado a nivel de solicitud

Casos de Uso Comunes

  • Compartir pool de conexiones a base de datos
  • Compartir configuración de aplicación
  • Compartir instancias de caché
  • Compartir clientes API
  • Seguimiento de contadores o estado global

La ventaja de Estado Afijo radica en su simplicidad y flexibilidad, permitiendo compartir fácilmente cualquier tipo de datos entre diferentes rutas y manejadores sin necesidad de código repetitivo extenso. Código de Ejemplo

main.rs
Cargo.toml
use std::sync::Arc;
use std::sync::Mutex;

use salvo::prelude::*;

// Configuration structure with username and password
#[allow(dead_code)]
#[derive(Default, Clone, Debug)]
struct Config {
    username: String,
    password: String,
}

// State structure to hold a list of fail messages
#[derive(Default, Debug)]
struct State {
    fails: Mutex<Vec<String>>,
}

#[handler]
async fn hello(depot: &mut Depot) -> String {
    // Obtain the Config instance from the depot
    let config = depot.obtain::<Config>().unwrap();
    // Get custom data from the depot
    let custom_data = depot.get::<&str>("custom_data").unwrap();
    // Obtain the shared State instance from the depot
    let state = depot.obtain::<Arc<State>>().unwrap();
    // Lock the fails vector and add a new fail message
    let mut fails_ref = state.fails.lock().unwrap();
    fails_ref.push("fail message".into());
    // Format and return the response string
    format!("Hello World\nConfig: {config:#?}\nFails: {fails_ref:#?}\nCustom Data: {custom_data}")
}

#[tokio::main]
async fn main() {
    // Initialize the tracing subscriber for logging
    tracing_subscriber::fmt().init();

    // Create a Config instance with default username and password
    let config = Config {
        username: "root".to_string(),
        password: "pwd".to_string(),
    };

    // Set up the router with state injection and custom data
    let router = Router::new()
        // Use hoop to inject middleware and data into the request context
        .hoop(
            affix_state::inject(config)
                // Inject a shared State instance into the request context
                .inject(Arc::new(State {
                    fails: Mutex::new(Vec::new()),
                }))
                // Insert custom data into the request context
                .insert("custom_data", "I love this world!"),
        )
        // Register the hello handler for the root path
        .get(hello)
        // Add an additional route for the path "/hello" with the same handler
        .push(Router::with_path("hello").get(hello));

    // Bind the server to port 8698 and start serving
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(router).await;
}