为何在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要求,或者存在类型匹配、路由配置问题。
解决步骤
- 为
User添加必要的trait
Axum的Extension提取器要求被提取类型必须实现Send + Sync + 'static,打开models/users.rs,在User结构体定义上补充相关trait:
#[derive(Queryable, Debug, Clone, Send, Sync)] pub struct User { // 你的字段定义 }
验证类型一致性
检查控制器和中间件中导入的User是否为同一类型,确保两处的use crate::models::users::User;导入路径完全一致,避免出现同名不同模块的类型冲突。修复同步数据库操作的异步调用(可选但推荐)
中间件中的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)?;
- 修正路由路径冲突
当前代码将未保护路由和保护路由都挂载到/路径,会导致路由匹配冲突,建议给保护路由设置独立路径:
let protected_route = Router::new() .route("/users", get(get_users)) .layer(middleware::from_fn_with_state( app_state.clone(), jwt_authentification, ));
内容的提问来源于stack exchange,提问作者user13103631
相关产品推荐
相关产品推荐

