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

为何在Axum处理器中添加Extension获取认证用户会编译失败?

Axum JWT路由保护编译错误问题

问题背景

使用Axum构建Web服务器,通过JWT保护部分路由,编写了中间件从请求中获取并验证token,将用户数据传递给处理器函数,但引入Extension<User>后编译报错。

错误信息

the trait bound `fn(axum::extract::State<AppState>, Extension<User>) -> impl std::future::Future<Output = (axum::http::StatusCode, axum::Json<Vec<User>>)> {controllers::users::get_users}: Handler<_, _, _>` is not satisfied
the following other types implement trait `Handler<T, S, B>`:
  <MethodRouter<S, B> as Handler<(), S, B>>
  <axum::handler::Layered<L, H, T, S, B, B2> as Handler<T, S, B2>>rustcClick for full compiler diagnostic
users.rs(41, 25): required by a bound introduced by this call
method_routing.rs(145, 16): required by a bound in `axum::routing::get`

相关代码

控制器代码

use axum::extract::Extension;
use axum::extract::Json;
use axum::extract::State;
use axum::http::StatusCode;
use axum::middleware;
use axum::response::{IntoResponse, Response};
use axum::routing::{get, post};
use axum::Router;
use diesel::result::Error::DatabaseError;

use crate::app_state::AppState;
use crate::middlewares::jwt_auth::jwt_authentification;
use crate::models::users::{NewUser, User};
use crate::services;

pub async fn create_user(
    State(shared_state): State<AppState>,
    Json(payload): Json<NewUser>,
) -> Response {
    let results = services::users::add_user(payload, shared_state.db_pool.clone());
    match results {
        Ok(users) => return (StatusCode::CREATED, Json(users)).into_response(),
        Err(DatabaseError(..)) => return StatusCode::BAD_REQUEST.into_response(),
        Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
    }
}

async fn get_users(
    State(shared_state): State<AppState>,
    Extension(user): Extension<User>,
) -> (StatusCode, Json<Vec<User>>) {
    let users = services::users::get_users(shared_state.db_pool.clone());

    (StatusCode::OK, Json(users))
}

pub fn return_routes(app_state: AppState) -> Router<AppState> {
    let unprotected_route = Router::new().route("/", post(create_user));
    let protected_route =
        Router::new()
            .route("/", get(get_users))
            .layer(middleware::from_fn_with_state(
                app_state.clone(),
                jwt_authentification,
            ));

    return Router::new()
        .merge(unprotected_route)
        .merge(protected_route);
}

JWT中间件代码

use crate::{app_state::AppState, models::users::UserClaims, services::users::get_user_by_ref};
use axum::{
    extract::State,
    http::{Request, StatusCode},
    middleware::Next,
    response::Response,
};
use jsonwebtoken::{DecodingKey, Validation};

const AUTHORIZATION: &str = "authorization";
const BEARER: &str = "Bearer ";

pub async fn jwt_authentification<B>(
    State(app_state): State<AppState>,
    mut request: Request<B>,
    next: Next<B>,
) -> Result<Response, StatusCode> {
    let authorization_header = match request.headers().get(AUTHORIZATION) {
        Some(v) => v,
        None => return Err(StatusCode::UNAUTHORIZED),
    };

    let authorization = match authorization_header.to_str() {
        Ok(v) => v,
        Err(_) => return Err(StatusCode::UNAUTHORIZED),
    };

    if !authorization.starts_with(BEARER) {
        return Err(StatusCode::UNAUTHORIZED);
    }

    let jwt_token = authorization.trim_start_matches(BEARER);

    let token_header = match jsonwebtoken::decode_header(&jwt_token) {
        Ok(header) => header,
        _ => return Err(StatusCode::UNAUTHORIZED),
    };

    // Get token header
    let user_claims = match jsonwebtoken::decode::<UserClaims>(
        jwt_token,
        &DecodingKey::from_secret(app_state.config.encryption.jwt_secret.as_bytes()),
        // &Validation::new(jsonwebtoken::Algorithm::HS256),
        &Validation::new(token_header.alg),
    ) {
        Ok(claims) => claims,
        _ => return Err(StatusCode::UNAUTHORIZED),
    };

    let user_ref = user_claims.claims.sub;
    let user = match get_user_by_ref(user_ref, app_state.db_pool) {
        Ok(user) => user,
        _ => return Err(StatusCode::INTERNAL_SERVER_ERROR),
    };

    request.extensions_mut().insert(user);
    Ok(next.run(request).await)
}

问题原因及解决方法

核心原因

编译错误是因为User类型未满足Axum Extension提取器的trait要求,或者存在类型匹配、路由配置问题。

解决步骤

  1. 为User添加必要的trait
    Axum的Extension提取器要求被提取类型必须实现Send + Sync + 'static,打开models/users.rs,在User结构体定义上补充相关trait:
#[derive(Queryable, Debug, Clone, Send, Sync)]
pub struct User {
    // 你的字段定义
}
  1. 验证类型一致性
    检查控制器和中间件中导入的User是否为同一类型,确保两处的use crate::models::users::User;导入路径完全一致,避免出现同名不同模块的类型冲突。

  2. 修复同步数据库操作的异步调用(可选但推荐)
    中间件中的get_user_by_ref是同步数据库操作,直接在async上下文调用会阻塞事件循环,改用tokio::spawn_blocking包裹:

let user = tokio::task::spawn_blocking(move || {
    get_user_by_ref(user_ref, app_state.db_pool)
})
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
  1. 修正路由路径冲突
    当前代码将未保护路由和保护路由都挂载到/路径,会导致路由匹配冲突,建议给保护路由设置独立路径:
let protected_route =
    Router::new()
        .route("/users", get(get_users))
        .layer(middleware::from_fn_with_state(
            app_state.clone(),
            jwt_authentification,
        ));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:17:11