Affix State 請求中共享數據

Affix State 中介軟體用於往 Depot 中添加共享數據。 要使用 Affix State 功能,需要在 Cargo.toml 中開啟 affix-state feature

功能解析

Affix State 提供了一種在請求處理過程中共享數據的簡單方式。它允許你:

在路由配置階段注入全局配置或共享數據 在任何處理器中通過 Depot 訪問這些數據 支援任何可克隆的類型作為狀態數據

與其他框架對比 快速理解概念

框架語言狀態管理方式
Salvo (Affix State)Rust通過 Depot 存儲和訪問,支援多種類型
AxumRust通過 Extension 存儲狀態,類似但使用方式不同
Actix-webRust使用 App Data 和 Web::Data 共享狀態
GinGo使用 context.Set 和 context.Get 存取數據
EchoGo使用 context.Set 和 context.Get 管理共享狀態
SpringJava使用 ApplicationContext 或 @Bean 註解管理依賴
QuarkusJava使用 CDI 和依賴注入機制
Express.jsJavaScript使用 app.locals 或 req.app.locals 存儲全局狀態
Nest.jsJavaScript使用依賴注入系統管理共享服務
Koa.jsJavaScript使用 ctx.state 存儲請求級別狀態

常見使用場景

  • 數據庫連接池共享
  • 應用配置共享
  • 緩存實例共享
  • API 客戶端共享
  • 全局計數器或狀態追蹤

Affix State 的優勢在於它的簡潔性和靈活性,可以輕鬆地在不同路由和處理器之間共享任何類型的數據,而無需大量的樣板代碼。 示例代碼

main.rs
Cargo.toml
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;
}