Stato Affix - Condivisione Dati nelle Richieste
Il middleware Affix State permette di aggiungere dati condivisi al Depot.
Per utilizzare la funzionalità Affix State, è necessario abilitare la feature affix-state
in Cargo.toml.
Analisi Funzionale
Affix State offre un modo semplice per condividere dati durante l'elaborazione delle richieste. Consente di:
Iniettare configurazioni globali o dati condivisi durante la fase di configurazione delle route
Accedere a questi dati tramite Depot in qualsiasi handler
Supportare qualsiasi tipo clonabile come dati di stato
Confronto con Altri Framework - Comprensione Rapida dei Concetti
Framework | Linguaggio | Gestione dello Stato |
---|
Salvo (Affix State) | Rust | Memorizzazione e accesso tramite Depot, supporta vari tipi |
Axum | Rust | Stato memorizzato tramite Extension, simile ma con approccio diverso |
Actix-web | Rust | Utilizza App Data e Web::Data per condividere lo stato |
Gin | Go | Usa context.Set e context.Get per accedere ai dati |
Echo | Go | Gestisce lo stato condiviso con context.Set e context.Get |
Spring | Java | Gestisce le dipendenze con ApplicationContext o annotazione @Bean |
Quarkus | Java | Utilizza CDI e meccanismi di dependency injection |
Express.js | JavaScript | Memorizza lo stato globale in app.locals o req.app.locals |
Nest.js | JavaScript | Gestisce servizi condivisi con sistema di dependency injection |
Koa.js | JavaScript | Memorizza lo stato a livello di richiesta in ctx.state |
Scenari d'Uso Comuni
- Condivisione pool di connessioni al database
- Configurazioni applicative condivise
- Istanza di cache condivisa
- Client API condivisi
- Contatori globali o tracciamento dello stato
Il vantaggio di Affix State risiede nella sua semplicità e flessibilità, permettendo di condividere facilmente dati di qualsiasi tipo tra diverse route e handler senza necessità di codice ripetitivo.
Codice di Esempio
affix-state/src/main.rs
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 5800 and start serving
let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
Server::new(acceptor).serve(router).await;
}