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`
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.