diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 33f45d47..1bce75c7 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -12,9 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # 0.6.18 (30. April, 2023) - **fixed:** Don't remove the `Sec-WebSocket-Key` header in `WebSocketUpgrade` ([#1972]) +- **added:** Add `axum::extract::Query::try_from_uri` ([#2058]) - **added:** Implement `IntoResponse` for `Box` and `Box<[u8]>` ([#2035]) [#1972]: https://github.com/tokio-rs/axum/pull/1972 +[#2058]: https://github.com/tokio-rs/axum/pull/2058 # 0.6.17 (25. April, 2023) diff --git a/axum/src/extract/query.rs b/axum/src/extract/query.rs index f9551e07..6f8cb89d 100644 --- a/axum/src/extract/query.rs +++ b/axum/src/extract/query.rs @@ -1,6 +1,6 @@ use super::{rejection::*, FromRequestParts}; use async_trait::async_trait; -use http::request::Parts; +use http::{request::Parts, Uri}; use serde::de::DeserializeOwned; /// Extractor that deserializes query strings into some type. @@ -57,10 +57,38 @@ where type Rejection = QueryRejection; async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - let query = parts.uri.query().unwrap_or_default(); - let value = + Self::try_from_uri(&parts.uri) + } +} + +impl Query +where + T: DeserializeOwned, +{ + /// Attempts to construct a [`Query`] from a reference to a [`Uri`]. + /// + /// # Example + /// ``` + /// use axum::extract::Query; + /// use http::Uri; + /// use serde::Deserialize; + /// + /// #[derive(Deserialize)] + /// struct ExampleParams { + /// foo: String, + /// bar: u32, + /// } + /// + /// let uri: Uri = "http://example.com/path?foo=hello&bar=42".parse().unwrap(); + /// let result: Query = Query::try_from_uri(&uri).unwrap(); + /// assert_eq!(result.foo, String::from("hello")); + /// assert_eq!(result.bar, 42); + /// ``` + pub fn try_from_uri(value: &Uri) -> Result { + let query = value.query().unwrap_or_default(); + let params = serde_urlencoded::from_str(query).map_err(FailedToDeserializeQueryString::from_err)?; - Ok(Query(value)) + Ok(Query(params)) } } @@ -136,4 +164,32 @@ mod tests { let res = client.get("/?n=hi").send().await; assert_eq!(res.status(), StatusCode::BAD_REQUEST); } + + #[test] + fn test_try_from_uri() { + #[derive(Deserialize)] + struct TestQueryParams { + foo: String, + bar: u32, + } + let uri: Uri = "http://example.com/path?foo=hello&bar=42".parse().unwrap(); + let result: Query = Query::try_from_uri(&uri).unwrap(); + assert_eq!(result.foo, String::from("hello")); + assert_eq!(result.bar, 42); + } + + #[test] + fn test_try_from_uri_with_invalid_query() { + #[derive(Deserialize)] + struct TestQueryParams { + _foo: String, + _bar: u32, + } + let uri: Uri = "http://example.com/path?foo=hello&bar=invalid" + .parse() + .unwrap(); + let result: Result, _> = Query::try_from_uri(&uri); + + assert!(result.is_err()); + } }