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