From f4063c229fc9974815512e7f401222f9c513e6a8 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 11 May 2022 11:01:30 +0200 Subject: [PATCH 1/5] Improve `Path` error --- axum/src/extract/path/mod.rs | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 0dc0167b61..97d7d25264 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -319,11 +319,19 @@ impl fmt::Display for ErrorKind { match self { ErrorKind::Message(error) => error.fmt(f), ErrorKind::InvalidUtf8InPathParam { key } => write!(f, "Invalid UTF-8 in `{}`", key), - ErrorKind::WrongNumberOfParameters { got, expected } => write!( - f, - "Wrong number of parameters. Expected {} but got {}", - expected, got - ), + ErrorKind::WrongNumberOfParameters { got, expected } => { + write!( + f, + "Wrong number of type parameters to `Path`. Expected {} but got {}", + expected, got + )?; + + if *expected == 1 { + write!(f, ". Note that multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path`")?; + } + + Ok(()) + } ErrorKind::UnsupportedType { name } => write!(f, "Unsupported type `{}`", name), ErrorKind::ParseErrorAtKey { key, @@ -368,14 +376,13 @@ impl IntoResponse for FailedToDeserializePathParams { let (status, body) = match self.0.kind { ErrorKind::Message(_) | ErrorKind::InvalidUtf8InPathParam { .. } - | ErrorKind::WrongNumberOfParameters { .. } | ErrorKind::ParseError { .. } | ErrorKind::ParseErrorAtIndex { .. } | ErrorKind::ParseErrorAtKey { .. } => ( StatusCode::BAD_REQUEST, format!("Invalid URL: {}", self.0.kind), ), - ErrorKind::UnsupportedType { .. } => { + ErrorKind::WrongNumberOfParameters { .. } | ErrorKind::UnsupportedType { .. } => { (StatusCode::INTERNAL_SERVER_ERROR, self.0.kind.to_string()) } }; @@ -539,4 +546,19 @@ mod tests { let res = client.get("/foo%20bar").send().await; assert_eq!(res.text().await, "foo bar"); } + + #[tokio::test] + async fn two_path_extractors() { + let app = Router::new().route("/:a/:b", get(|_: Path, _: Path| async {})); + + let client = TestClient::new(app); + + let res = client.get("/a/b").send().await; + assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!( + res.text().await, + "Wrong number of type parameters to `Path`. Expected 1 but got 2. \ + Note that multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path`", + ); + } } From 234af763f838f4d3173faa8417079d6bcf5e040e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 11 May 2022 11:04:44 +0200 Subject: [PATCH 2/5] Improve docs --- axum/src/extract/path/mod.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 97d7d25264..6c28e4537c 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -66,8 +66,7 @@ use std::{ /// ``` /// /// Path segments also can be deserialized into any type that implements -/// [`serde::Deserialize`]. Path segment labels will be matched with struct -/// field names. +/// [`serde::Deserialize`]. This includes tuples and structs: /// /// ```rust,no_run /// use axum::{ @@ -78,6 +77,7 @@ use std::{ /// use serde::Deserialize; /// use uuid::Uuid; /// +/// // Path segment labels will be matched with struct field names /// #[derive(Deserialize)] /// struct Params { /// user_id: Uuid, @@ -90,7 +90,17 @@ use std::{ /// // ... /// } /// -/// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show)); +/// // When using tuples the path segments will be matched by their position in the route +/// async fn users_teams_create( +/// Path((user_id, team_id)): Path<(String, String)>, +/// ) { +/// // ... +/// } +/// +/// let app = Router::new().route( +/// "/users/:user_id/team/:team_id", +/// get(users_teams_show).post(users_teams_create), +/// ); /// # async { /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; From dca2d0354f365a2f2d09fe3c592c1ee824cdab9b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 11 May 2022 11:11:18 +0200 Subject: [PATCH 3/5] changelog --- axum/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 1f20a58f50..9dfdee5428 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -7,7 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- None. +- **fixed:** Improve error for `PathRejection::WrongNumberOfParameters` to hint at using + `Path<(String, String)>` or `Path` ([#1023]) +- **fixed:** `PathRejection::WrongNumberOfParameters` now uses `500 Internal Server Error` since + its a programmer error and not a client error ([#1023]) + +[#1023]: https://github.com/tokio-rs/axum/pull/1023 # 0.5.5 (10. May, 2022) From 78ed08db24a2a6b6d35eec09c1c5d843358c4ba0 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 11 May 2022 11:15:46 +0200 Subject: [PATCH 4/5] Update axum/src/extract/path/mod.rs Co-authored-by: Jonas Platte --- axum/src/extract/path/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 6c28e4537c..23c77021be 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -332,7 +332,7 @@ impl fmt::Display for ErrorKind { ErrorKind::WrongNumberOfParameters { got, expected } => { write!( f, - "Wrong number of type parameters to `Path`. Expected {} but got {}", + "Wrong number of path arguments for `Path`. Expected {} but got {}", expected, got )?; From 2225e320c5afb6f7e1c593dfa93d2a3a4f3dba1e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Wed, 11 May 2022 11:17:47 +0200 Subject: [PATCH 5/5] fix test --- axum/src/extract/path/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 23c77021be..540ff8a730 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -567,7 +567,7 @@ mod tests { assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!( res.text().await, - "Wrong number of type parameters to `Path`. Expected 1 but got 2. \ + "Wrong number of path arguments for `Path`. Expected 1 but got 2. \ Note that multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path`", ); }