#Affix State リクエスト間でのデータ共有
Affix State ミドルウェアは、Depot に共有データを追加するために使用されます。
Affix State 機能を使用するには、Cargo.toml で affix-state フィーチャーを有効にする必要があります。
#機能の解説
Affix State は、リクエスト処理プロセス中にデータを共有するためのシンプルな方法を提供します。これにより以下が可能になります:
- ルート設定段階でグローバル設定や共有データを注入
- 任意のハンドラ内で Depot を通じてこれらのデータにアクセス
- クローン可能な任意の型を状態データとしてサポート
#他のフレームワークとの比較による概念の迅速な理解
| フレームワーク | 言語 | 状態管理方法 |
|---|---|---|
| Salvo (Affix State) | Rust | Depot による保存とアクセス、複数の型をサポート |
| Axum | Rust | Extension による状態保存、類似しているが使用方法が異なる |
| Actix-web | Rust | App Data と Web::Data による状態共有 |
| Gin | Go | context.Set と context.Get によるデータの保存・取得 |
| Echo | Go | context.Set と context.Get による共有状態の管理 |
| Spring | Java | ApplicationContext または @Bean アノテーションによる依存関係管理 |
| Quarkus | Java | CDI と依存性注入メカニズム |
| Express.js | JavaScript | app.locals または req.app.locals によるグローバル状態の保存 |
| Nest.js | JavaScript | 依存性注入システムによる共有サービスの管理 |
| Koa.js | JavaScript | ctx.state によるリクエストレベルの状態保存 |
#一般的な使用シナリオ
- データベース接続プールの共有
- アプリケーション設定の共有
- キャッシュインスタンスの共有
- API クライアントの共有
- グローバルカウンターや状態追跡
Affix State の利点は、その簡潔さと柔軟性にあります。大量の定型コードを必要とせず、異なるルートやハンドラ間で任意の型のデータを簡単に共有できます。 サンプルコード
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;
}
[package]
name = "example-affix-state"
version.workspace = true
edition.workspace = true
publish.workspace = true
rust-version.workspace = true
[dependencies]
salvo = { workspace = true, features = ["affix-state"] }
tokio = { workspace = true, features = ["macros"] }
tracing.workspace = true
tracing-subscriber.workspace = true