4

I am building an handler that requires state to be injected and also needs to extract the query parameters.

I started off by only extracting the state and that worked. The code for that looks something like this:

    #[derive(ValueEnum, Clone, Debug, serde::Deserialze, serde::Serialize)]
    pub enum MyParams {
       Normal,
       Verbose,
    }

    #[derive(Debug)]
   pub struct MyState {
      port: u16,
   }
    
    pub async fn serve(self) {
        let port = self.port;
        let app = Router::new()
            .route("/path", axum::routing::get(path))
            .with_state(Arc::new(self));

        let addr = SocketAddr::from(([127, 0, 0, 1], port));
        axum::Server::bind(&addr)
            .serve(app.into_make_service())
            .await
            .unwrap();
    }

async fn path(State(middle_ware): State<Arc<MyState>>) -> impl IntoResponse {
    let result = middle_ware.process().await;
    (StatusCode::OK, Json(result))
}

Now I want to extract query parameters so I updated the code as follows:

async fn path(State(middle_ware): State<Arc<MyState>>, params: Query<MyParams>) -> impl IntoResponse {
    println!("{:?}", params);
    let result = middle_ware.process().await;
    (StatusCode::OK, Json(result))
}

But this fails to compile with the error

    |
24  |             .route("/path", axum::routing::get(path))
    |                                  ------------------ ^^^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(State<Arc<MyState>>, Query<MyParams>) -> impl futures::Future<Output = impl IntoResponse> {path}`
    |                                  |
    |                                  required by a bound introduced by this call

Any ideas what to do, to be able to use both axum::extract::Query and axum::extract::State with Axum?

Finlay Weber
  • 2,989
  • 3
  • 17
  • 37

1 Answers1

6

The documentation provides the following example:

https://docs.rs/axum/latest/axum/extract/index.html#applying-multiple-extractors

PS: Don't forget to take care of the order of the extractors.

use axum::{
    extract::{Path, Query},
    routing::get,
    Router,
};
use uuid::Uuid;
use serde::Deserialize;

let app = Router::new().route("/users/:id/things", get(get_user_things));

#[derive(Deserialize)]
struct Pagination {
    page: usize,
    per_page: usize,
}

impl Default for Pagination {
    fn default() -> Self {
        Self { page: 1, per_page: 30 }
    }
}

async fn get_user_things(
    Path(user_id): Path<Uuid>,
    pagination: Option<Query<Pagination>>,
) {
    let Query(pagination) = pagination.unwrap_or_default();

    // ...
}
Frank Moreno
  • 304
  • 1
  • 7