You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Axum+Leptos中,如何在#[server]宏中获取AppState?

解决Leptos #[server]宏中获取Axum AppState的问题

核心说明

Leptos的#[server]宏生成的处理器和原生Axum处理器的参数逻辑不同,不能直接用State<AppState>作为函数参数。下面提供两种可行的解决方案:

方法一:全局静态存储AppState

这种方式简单直接,适合全局共享的静态状态。

  1. 定义全局状态存储与初始化方法
use leptos::*;
use std::sync::OnceLock;

#[derive(Clone)]
pub struct AppState {
    // 示例:数据库连接池、配置等状态字段
    pub db_pool: sqlx::PgPool,
}

// 全局静态变量存储AppState,保证初始化一次
static GLOBAL_APP_STATE: OnceLock<AppState> = OnceLock::new();

// 初始化全局状态
pub fn init_global_state(state: AppState) {
    GLOBAL_APP_STATE.set(state).expect("AppState只能初始化一次");
}

// 获取全局状态的工具函数
pub fn get_global_state() -> &'static AppState {
    GLOBAL_APP_STATE.get().expect("AppState未完成初始化")
}
  1. 在#[server]函数中直接调用获取
#[server(Something, "/api")]
pub async fn something(cx: Scope, arg: MyStruct) -> Result<String, ServerFnError> {
    let state = get_global_state();
    // 这里可以使用state.db_pool等资源处理业务逻辑
    Ok("处理完成".to_string())
}
  1. Axum启动时初始化全局状态
#[tokio::main]
async fn main() {
    // 创建AppState实例
    let db_pool = sqlx::PgPool::connect("postgres://user:pass@localhost/db").await.unwrap();
    let app_state = AppState { db_pool };
    
    // 初始化全局状态
    init_global_state(app_state.clone());

    // 构建Axum路由
    let app = axum::Router::new()
        .route("/api/*fn_name", leptos_axum::handle_server_fns())
        .leptos_routes(/* 你的Leptos SSR路由配置 */)
        .with_state(app_state);

    // 启动服务
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_svc())
        .await.unwrap();
}

方法二:通过Axum中间件注入Leptos上下文

这种方式更贴合Axum的请求处理逻辑,支持请求级别的上下文传递。

  1. 为AppState实现Leptos上下文兼容
use leptos::*;

#[derive(Clone, Debug)]
pub struct AppState {
    pub db_pool: sqlx::PgPool,
}

// 实现Display trait满足Leptos上下文要求
impl std::fmt::Display for AppState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "AppState")
    }
}
  1. 编写中间件注入上下文
use axum::{middleware::Next, http::Request, extract::State};
use leptos_axum::RequestParts;

async fn inject_state_to_context<B>(
    State(app_state): State<AppState>,
    mut req: Request<B>,
    next: Next<B>,
) -> axum::response::Response {
    // 从请求扩展中获取Leptos的RequestParts,注入AppState
    if let Some(parts) = req.extensions_mut().get_mut::<RequestParts>() {
        parts.insert_context(app_state.clone());
    }
    next.run(req).await
}
  1. 在#[server]函数中通过上下文获取
#[server(Something, "/api")]
pub async fn something(cx: Scope, arg: MyStruct) -> Result<String, ServerFnError> {
    let app_state = use_context::<AppState>(cx)
        .ok_or_else(|| ServerFnError::ServerError("AppState未注入上下文".to_string()))?;
    
    // 使用app_state处理业务逻辑
    Ok("处理成功".to_string())
}
  1. Axum路由中应用中间件
#[tokio::main]
async fn main() {
    let db_pool = sqlx::PgPool::connect("postgres://user:pass@localhost/db").await.unwrap();
    let app_state = AppState { db_pool };

    let app = axum::Router::new()
        .route("/api/*fn_name", leptos_axum::handle_server_fns())
        .layer(axum::middleware::from_fn(inject_state_to_context)) // 应用注入中间件
        .leptos_routes(/* 你的Leptos SSR路由配置 */)
        .with_state(app_state);

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_svc())
        .await.unwrap();
}

两种方案对比

  • 全局静态方案:实现简单,适合不会动态变更的全局状态,但无法支持请求级别的状态调整;
  • 上下文注入方案:更灵活,支持请求维度的状态传递,适合需要根据请求动态处理的场景;
  • 两种方案都要求AppState实现Clone,因为Leptos上下文和Axum状态传递都需要克隆实例。

内容的提问来源于stack exchange,提问作者g4borg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:35:21