Attribute Macro axum::debug_handler

source ·
#[debug_handler]
Available on crate feature macros only.
Expand description

Generates better error messages when applied handler functions.

While using axum, you can get long error messages for simple mistakes. For example:

use axum::{routing::get, Router};

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(handler));

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

fn handler() -> &'static str {
    "Hello, world"
}

You will get a long error message about function not implementing Handler trait. But why does this function not implement it? To figure it out, the debug_handler macro can be used.

#[debug_handler]
fn handler() -> &'static str {
    "Hello, world"
}
error: handlers must be async functions
  --> main.rs:xx:1
   |
xx | fn handler() -> &'static str {
   | ^^

As the error message says, handler function needs to be async.

use axum::{routing::get, Router, debug_handler};

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(handler));

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

#[debug_handler]
async fn handler() -> &'static str {
    "Hello, world"
}

Changing request body type

By default #[debug_handler] assumes your request body type is axum::body::Body. This will work for most extractors but, for example, it wont work for Request<axum::body::BoxBody>, which only implements FromRequest<BoxBody> and not FromRequest<Body>.

To work around that the request body type can be customized like so:

use axum::{body::BoxBody, http::Request, debug_handler};

#[debug_handler(body = BoxBody)]
async fn handler(request: Request<BoxBody>) {}

Changing state type

By default #[debug_handler] assumes your state type is () unless your handler has a axum::extract::State argument:

use axum::{debug_handler, extract::State};

#[debug_handler]
async fn handler(
    // this makes `#[debug_handler]` use `AppState`
    State(state): State<AppState>,
) {}

#[derive(Clone)]
struct AppState {}

If your handler takes multiple axum::extract::State arguments or you need to otherwise customize the state type you can set it with #[debug_handler(state = ...)]:

use axum::{debug_handler, extract::{State, FromRef}};

#[debug_handler(state = AppState)]
async fn handler(
    State(app_state): State<AppState>,
    State(inner_state): State<InnerState>,
) {}

#[derive(Clone)]
struct AppState {
    inner: InnerState,
}

#[derive(Clone)]
struct InnerState {}

impl FromRef<AppState> for InnerState {
    fn from_ref(state: &AppState) -> Self {
        state.inner.clone()
    }
}

Limitations

This macro does not work for functions in an impl block that don’t have a self parameter:

use axum::{debug_handler, extract::Path};

struct App {}

impl App {
    #[debug_handler]
    async fn handler(Path(_): Path<String>) {}
}

This will yield an error similar to this:

error[E0425]: cannot find function `__axum_macros_check_handler_0_from_request_check` in this scope

Performance

This macro has no effect when compiled with the release profile. (eg. cargo build --release)