Estado de Afixo - Dados Compartilhados em Requisições

O middleware Estado de Afixo é utilizado para adicionar dados compartilhados ao Depot. Para utilizar o recurso Estado de Afixo, é necessário habilitar o recurso affix-state no Cargo.toml.

Análise do Recurso

O Estado de Afixo oferece uma maneira simples de compartilhar dados durante o processamento de requisições. Ele permite que você:

  • Injete configurações globais ou dados compartilhados durante a configuração de rotas
  • Acesse esses dados via Depot em qualquer manipulador
  • Suporte qualquer tipo clonável como dados de estado

Comparação com Outros Frameworks para Compreensão Rápida do Conceito

FrameworkLinguagemAbordagem de Gerenciamento de Estado
Salvo (Estado de Afixo)RustArmazena e acessa via Depot, suporta múltiplos tipos
AxumRustArmazena estado via Extension, similar mas com uso diferente
Actix-webRustUtiliza App Data e Web::Data para compartilhar estado
GinGoUsa context.Set e context.Get para armazenar e recuperar dados
EchoGoUsa context.Set e context.Get para gerenciar estado compartilhado
SpringJavaUtiliza ApplicationContext ou anotações @Bean para gerenciamento de dependências
QuarkusJavaUsa mecanismos CDI e injeção de dependência
Express.jsJavaScriptUsa app.locals ou req.app.locals para armazenar estado global
Nest.jsJavaScriptUtiliza um sistema de injeção de dependência para gerenciar serviços compartilhados
Koa.jsJavaScriptUsa ctx.state para armazenar estado no nível da requisição

Casos de Uso Comuns

  • Compartilhamento de pool de conexões de banco de dados
  • Compartilhamento de configurações da aplicação
  • Compartilhamento de instâncias de cache
  • Compartilhamento de clientes de API
  • Contadores globais ou rastreamento de estado

A vantagem do Estado de Afixo está na sua simplicidade e flexibilidade, permitindo o compartilhamento fácil de qualquer tipo de dados entre diferentes rotas e manipuladores sem a necessidade de código repetitivo extensivo. Código de Exemplo

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