在Rust的Axum中,可以选择在路由路径中使用或不使用尾随斜杠。

huangapple go评论65阅读模式
英文:

Route paths with or without of trailing slashes in rust/axum

问题

我想将http://0.0.0.0/foohttp: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(&quot;/foo&quot;, get(get_foo))
        .layer(NormalizePathLayer::trim_trailing_slash());

    let port_str = std::env::var(&quot;PORT&quot;).unwrap_or(&quot;8000&quot;.to_owned());
    let port = port_str.parse::&lt;u16&gt;().unwrap();
    let addr = SocketAddr::from(([0, 0, 0, 0], port));
    println!(&quot;listening on http://{}&quot;, addr);
    axum::Server::bind(&amp;addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn get_foo() -&gt; Result&lt;String, StatusCode&gt;  {
    Ok(&quot;Hello from foo.&quot;.to_owned())
}

... and accompanying Cargo.toml:

[package]
name = &quot;axum_trailing_slash&quot;
version = &quot;0.1.0&quot;
edition = &quot;2021&quot;

[dependencies]
axum = { version = &quot;0.6&quot; }
tokio = { version = &quot;1.0&quot;, features = [&quot;full&quot;] }
tower = { version = &quot;0.4&quot; }
tower-http = { version = &quot;0.3&quot;, features = [&quot;normalize-path&quot;] }

答案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(&quot;/foo&quot;, 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(&quot;/foo&quot;, 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&lt;S, B&gt;
where
    B: HttpBody + Send + &#39;static,
    S: Clone + Send + Sync + &#39;static,
{
    fn directory_route(self, path: &amp;str, method_router: MethodRouter&lt;S, B&gt;) -&gt; Self;
}

impl&lt;S, B&gt; RouterExt&lt;S, B&gt; for Router&lt;S, B&gt;
where
    B: HttpBody + Send + &#39;static,
    S: Clone + Send + Sync + &#39;static,
{
    fn directory_route(self, path: &amp;str, method_router: MethodRouter&lt;S, B&gt;) -&gt; Self {
        self.route(path, method_router.clone())
            .route(&amp;format!(&quot;{path}/&quot;), method_router)
    }
}

Then add routes like let app = Router::new().directory_route(&quot;/foo&quot;, get(get_foo).
It will add both the /foo and /foo/ routes.

huangapple
  • 本文由 发表于 2023年2月6日 06:01:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75355826.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定