在 Axum 里,共享状态最推荐的方式是 State<T>。
use axum::{extract::State, routing::get, Router};
use std::sync::Arc;
#[derive(Clone)]
struct AppState {
service_name: String,
}
async fn health(State(state): State<Arc<AppState>>) -> String {
format!("{} OK", state.service_name)
}
let shared_state = Arc::new(AppState {
service_name: "north-gate".to_owned(),
});
let app = Router::new()
.route("/health", get(health))
.with_state(shared_state);这张图很重要:
State
= 整座总关共享的底册
= 每个请求都能查
= 适合放数据库连接池、配置、客户端、缓存句柄但官方文档也明确提醒了一件很容易踩坑的事:
with_state 提供的是全局状态,不适合放“从请求里临时提取出来的数据”,比如鉴权后的当前用户。那类数据应该放 Extension。
也就是说:
总关底册(全局共享)
-> State
某辆车本次通关时临时贴上的标签
-> Extension比如:
State<AppState>:数据库池、配置、Redis 客户端Extension<AuthContext>:当前关员、当前租户、当前请求 trace 数据
#一个进阶细节:Router<S> 不是“拥有状态 S”,而是“还缺状态 S”
这点是 Axum 新手高频误区。
很多人看到 Router<AppState> 会以为是“这个 router 已经有了 AppState”。
其实更准确的意思是:
Router<AppState>
= 这张关口图要真正接车,还缺一个 AppState所以:
- 先写
fn routes() -> Router<AppState>很常见 - 最后在
main里.with_state(app_state)再落地,很正常
这个心智一旦顺了,你对 nest、merge、模块化路由的理解会轻很多。