diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 0379672c..54792597 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# Unreleased + +- **breaking:** The tuple and tuple_struct `Path` extractor deserializers now check that the number of parameters matches the tuple length exactly ([#2931]) + +[#2931]: https://github.com/tokio-rs/axum/pull/2931 + # 0.7.6 - **change:** Avoid cloning `Arc` during deserialization of `Path` diff --git a/axum/src/extract/path/de.rs b/axum/src/extract/path/de.rs index 8ba8a431..2d0b04f5 100644 --- a/axum/src/extract/path/de.rs +++ b/axum/src/extract/path/de.rs @@ -140,7 +140,7 @@ impl<'de> Deserializer<'de> for PathDeserializer<'de> { where V: Visitor<'de>, { - if self.url_params.len() < len { + if self.url_params.len() != len { return Err(PathDeserializationError::wrong_number_of_parameters() .got(self.url_params.len()) .expected(len)); @@ -160,7 +160,7 @@ impl<'de> Deserializer<'de> for PathDeserializer<'de> { where V: Visitor<'de>, { - if self.url_params.len() < len { + if self.url_params.len() != len { return Err(PathDeserializationError::wrong_number_of_parameters() .got(self.url_params.len()) .expected(len)); @@ -773,20 +773,6 @@ mod tests { ); } - #[test] - fn test_parse_tuple_ignoring_additional_fields() { - let url_params = create_url_params(vec![ - ("a", "abc"), - ("b", "true"), - ("c", "1"), - ("d", "false"), - ]); - assert_eq!( - <(&str, bool, u32)>::deserialize(PathDeserializer::new(&url_params)).unwrap(), - ("abc", true, 1) - ); - } - #[test] fn test_parse_map() { let url_params = create_url_params(vec![("a", "1"), ("b", "true"), ("c", "abc")]); @@ -813,6 +799,18 @@ mod tests { }; } + #[test] + fn test_parse_tuple_too_many_fields() { + test_parse_error!( + vec![("a", "abc"), ("b", "true"), ("c", "1"), ("d", "false"),], + (&str, bool, u32), + ErrorKind::WrongNumberOfParameters { + got: 4, + expected: 3, + } + ); + } + #[test] fn test_wrong_number_of_parameters_error() { test_parse_error!( diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 330e270e..fda8364c 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -747,6 +747,33 @@ mod tests { ); } + #[crate::test] + async fn tuple_param_matches_exactly() { + #[allow(dead_code)] + #[derive(Deserialize)] + struct Tuple(String, String); + + let app = Router::new() + .route("/foo/:a/:b/:c", get(|_: Path<(String, String)>| async {})) + .route("/bar/:a/:b/:c", get(|_: Path| async {})); + + let client = TestClient::new(app); + + let res = client.get("/foo/a/b/c").await; + assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!( + res.text().await, + "Wrong number of path arguments for `Path`. Expected 2 but got 3", + ); + + let res = client.get("/bar/a/b/c").await; + assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!( + res.text().await, + "Wrong number of path arguments for `Path`. Expected 2 but got 3", + ); + } + #[crate::test] async fn deserialize_into_vec_of_tuples() { let app = Router::new().route(