I want to route both http://0.0.0.0/foo
and http://0.0.0.0/foo/
to the same get_foo
handler. However, in practice, only /foo
gets routed and /foo/
404s. I suspect I'm setting up/attaching the middleware wrong:
use axum::http::StatusCode;
use axum::{routing::{get}, Router};
use std::{net::SocketAddr};
use tower_http::normalize_path::NormalizePathLayer;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/foo", get(get_foo))
.layer(NormalizePathLayer::trim_trailing_slash());
let port_str = std::env::var("PORT").unwrap_or("8000".to_owned());
let port = port_str.parse::<u16>().unwrap();
let addr = SocketAddr::from(([0, 0, 0, 0], port));
println!("listening on http://{}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn get_foo() -> Result<String, StatusCode> {
Ok("Hello from foo.".to_owned())
}
... and accompanying Cargo.toml
:
[package]
name = "axum_trailing_slash"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = { version = "0.6" }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4" }
tower-http = { version = "0.3", features = ["normalize-path"] }
From the docs of Router::layer
:
Middleware added with this method will run after routing and thus cannot be used to rewrite the request URI. See “Rewriting request URI in middleware” for more details and a workaround.
The workaround is to wrap the middleware around the entire Router (this works because Router implements Service):
//…
use axum::ServiceExt;
use tower::layer::Layer;
#[tokio::main]
async fn main() {
let app =
NormalizePathLayer::trim_trailing_slash().layer(Router::new().route("/foo", get(get_foo)));
//…
}
With axum>=0.7
you might need to use a fully qualified path with app.into_make_service
like:
use axum::extract::Request;
axum::Server::bind(&addr)
.serve(ServiceExt::<Request>::into_make_service(app))
.await
.unwrap();