Search code examples
rustmiddlewaretraitsrust-axum

How to fix Service trait error when using from_fn_with_state for middleware?


I'm creating a middleware function that authenticates the incoming request. I've read the docs for Axum and some other examples. I'm stuck on a compiler trait error.

Here is the slimmed down code:

#[derive(Clone, Debug)]
pub struct AppState {
    pub db: mongodb::Database,
    pub s3_bucket_name: String,
    pub jwt_secret: String,
    pub api_key: String,
}

pub fn get_router(state: AppState) -> axum::Router {
    Router::new()
        .route("/*path", any(catch_any))
        .layer(from_fn_with_state(state.clone(), auth_guard))
        .with_state(Arc::new(state))
}

pub type JsonResponse = (StatusCode, Json<Value>);

pub async fn auth_guard(
    State(state): State<Arc<AppState>>,
    mut req: Request,
    next: Next,
) -> Result<impl IntoResponse, JsonResponse> {
    // jwt decoding and db lookup

    let auth_user = AuthUser::JWT((user.sso, user.user_id));
    req.extensions_mut().insert(auth_user);

    // pass the request onto the rest of the layers and routes
    Ok(next.run(req).await)
}

Now I'm getting the generic error that I'm not fulfilling the Service<Request> trait. I've tried various From or Into implementations with no success. Is there something I'm just missing?

error[E0277]: the trait bound `axum::middleware::FromFn<fn(axum::extract::State<Arc<AppState>>, http::Request<axum::body::Body>, axum::middleware::Next) -> impl futures::Future<Output = impl axum::response::IntoResponse> {auth_guard}, AppState, Route, _>: Service<http::Request<axum::body::Body>>` is not satisfied
   --> src\router.rs:105:10
    |
105 |         .layer(from_fn_with_state(state.clone(), auth_guard))
    |          ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Service<http::Request<axum::body::Body>>` is not implemented for `axum::middleware::FromFn<fn(axum::extract::State<Arc<AppState>>, http::Request<axum::body::Body>, axum::middleware::Next) -> impl futures::Future<Output = impl axum::response::IntoResponse> {auth_guard}, AppState, Route, _>`
    |          |
    |          required by a bound introduced by this call
    |
    = help: the following other types implement trait `Service<Request>`:
              axum::middleware::FromFn<F, S, I, (T1,)>
              axum::middleware::FromFn<F, S, I, (T1, T2)>
              axum::middleware::FromFn<F, S, I, (T1, T2, T3)>
              axum::middleware::FromFn<F, S, I, (T1, T2, T3, T4)>
              axum::middleware::FromFn<F, S, I, (T1, T2, T3, T4, T5)>
              axum::middleware::FromFn<F, S, I, (T1, T2, T3, T4, T5, T6)>
              axum::middleware::FromFn<F, S, I, (T1, T2, T3, T4, T5, T6, T7)>
              axum::middleware::FromFn<F, S, I, (T1, T2, T3, T4, T5, T6, T7, T8)>
note: required by a bound in `Router::<S>::layer`
   --> C:\Users\me\.cargo\registry\src\index.crates.io-6f17d22bba15001f\axum-0.7.5\src\routing\mod.rs:278:21
    |
275 |     pub fn layer<L>(self, layer: L) -> Router<S>
    |            ----- required by a bound in this associated function
...
278 |         L::Service: Service<Request> + Clone + Send + 'static,
    |                     ^^^^^^^^^^^^^^^^ required by this bound in `Router::<S>::layer`

Solution

  • Your state types do not match. You are providing an AppState to from_fn_with_state but your middleware function is expecting an Arc<AppState>.

    Assuming you don't want to needlessly clone the AppState, you would do this:

    pub fn get_router(state: AppState) -> Router {
        let state = Arc::new(state);
    
        Router::new()
            .route("/*path", any(catch_any))
            .layer(from_fn_with_state(state.clone(), auth_guard))
            .with_state(state)
    }
    

    In general, the rules for valid middleware is pretty much the same as those for handlers (see: Why does my axum handler not implement Handler?), with the addition that a Next parameter must be at the end. The documentation for from_fn largely covers it. Unfortunately there is not a #[debug_handler] equivalent for middleware functions.