英文:
Route paths with or without of trailing slashes in rust/axum
问题
我想将http://0.0.0.0/foo
和http:0.0.0.0/foo/
都路由到相同的get_foo
处理程序。然而,在实践中,只有/foo
被路由,/foo/
返回404。我怀疑我可能错误地设置/附加了中间件:
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())
}
... 以及相应的 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"] }
英文:
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"] }
答案1
得分: 6
来自Router::layer
文档:
使用此方法添加的中间件将在路由之后运行,因此不能用于重写请求的URI。有关更多详细信息和解决方法,请参见“在中间件中重写请求URI”。
解决方法是将中间件包装在整个Router周围(这样可以工作,因为Router实现了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)));
//…
}
英文:
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)));
//…
}
答案2
得分: 0
没有附加依赖或中间件的版本:
trait RouterExt<S, B>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
fn directory_route(self, path: &str, method_router: MethodRouter<S, B>) -> Self;
}
impl<S, B> RouterExt<S, B> for Router<S, B>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
fn directory_route(self, path: &str, method_router: MethodRouter<S, B>) -> Self {
self.route(path, method_router.clone())
.route(&format!("{path}/"), method_router)
}
}
然后添加路由,如 `let app = Router::new().directory_route("/foo", get(get_foo))`。
它将添加`/foo`和`/foo/`两个路由。
英文:
Version without an additional dependency or middleware:
trait RouterExt<S, B>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
fn directory_route(self, path: &str, method_router: MethodRouter<S, B>) -> Self;
}
impl<S, B> RouterExt<S, B> for Router<S, B>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
fn directory_route(self, path: &str, method_router: MethodRouter<S, B>) -> Self {
self.route(path, method_router.clone())
.route(&format!("{path}/"), method_router)
}
}
Then add routes like let app = Router::new().directory_route("/foo", get(get_foo)
.
It will add both the /foo
and /foo/
routes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论