Données d'état partagées dans les requêtes

Le middleware Affix State permet d'ajouter des données partagées au Depot. Pour utiliser la fonctionnalité Affix State, vous devez activer la fonction affix-state dans Cargo.toml.

Analyse de la fonctionnalité

Affix State offre un moyen simple de partager des données pendant le traitement des requêtes. Il vous permet de :

  • Injecter des configurations globales ou des données partagées lors de la configuration des routes
  • Accéder à ces données via le Depot dans n'importe quel gestionnaire
  • Prendre en charge tout type clonable comme données d'état

Comparaison avec d'autres frameworks pour une compréhension rapide du concept

FrameworkLangageApproche de gestion d'état
Salvo (Affix State)RustStocke et accède via Depot, prend en charge plusieurs types
AxumRustStocke l'état via Extension, similaire mais avec une utilisation différente
Actix-webRustUtilise App Data et Web::Data pour partager l'état
GinGoUtilise context.Set et context.Get pour stocker et récupérer des données
EchoGoUtilise context.Set et context.Get pour gérer l'état partagé
SpringJavaUtilise ApplicationContext ou les annotations @Bean pour la gestion des dépendances
QuarkusJavaUtilise les mécanismes CDI et d'injection de dépendances
Express.jsJavaScriptUtilise app.locals ou req.app.locals pour stocker l'état global
Nest.jsJavaScriptUtilise un système d'injection de dépendances pour gérer les services partagés
Koa.jsJavaScriptUtilise ctx.state pour stocker l'état au niveau de la requête

Cas d'utilisation courants

  • Partage de pool de connexions à la base de données
  • Partage de configuration d'application
  • Partage d'instance de cache
  • Partage de client API
  • Compteurs globaux ou suivi d'état

L'avantage d'Affix State réside dans sa simplicité et sa flexibilité, permettant un partage facile de tout type de données entre différentes routes et gestionnaires sans code répétitif excessif. Exemple de code

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;
}