Skip to content

Commit 0ed02a9

Browse files
dobecaddavidpdrsn
andauthored
Add axum::extract::Query::try_from_uri (#2058)
Co-authored-by: David Pedersen <[email protected]>
1 parent cc9629f commit 0ed02a9

File tree

2 files changed

+62
-4
lines changed

2 files changed

+62
-4
lines changed

axum/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5353
- **breaking:** Only inherit fallbacks for routers nested with `Router::nest`.
5454
Routers nested with `Router::nest_service` will no longer inherit fallbacks ([#1956])
5555
- **fixed:** Don't remove the `Sec-WebSocket-Key` header in `WebSocketUpgrade` ([#1972])
56+
- **added:** Add `axum::extract::Query::try_from_uri` ([#2058])
5657
- **added:** Implement `IntoResponse` for `Box<str>` and `Box<[u8]>` ([#2035])
5758

5859
[#1664]: https://github.com/tokio-rs/axum/pull/1664
@@ -64,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6465
[#1868]: https://github.com/tokio-rs/axum/pull/1868
6566
[#1956]: https://github.com/tokio-rs/axum/pull/1956
6667
[#1972]: https://github.com/tokio-rs/axum/pull/1972
68+
[#2058]: https://github.com/tokio-rs/axum/pull/2058
6769

6870
# 0.6.17 (25. April, 2023)
6971

axum/src/extract/query.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{rejection::*, FromRequestParts};
22
use async_trait::async_trait;
3-
use http::request::Parts;
3+
use http::{request::Parts, Uri};
44
use serde::de::DeserializeOwned;
55

66
/// Extractor that deserializes query strings into some type.
@@ -55,10 +55,38 @@ where
5555
type Rejection = QueryRejection;
5656

5757
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
58-
let query = parts.uri.query().unwrap_or_default();
59-
let value =
58+
Self::try_from_uri(&parts.uri)
59+
}
60+
}
61+
62+
impl<T> Query<T>
63+
where
64+
T: DeserializeOwned,
65+
{
66+
/// Attempts to construct a [`Query`] from a reference to a [`Uri`].
67+
///
68+
/// # Example
69+
/// ```
70+
/// use axum::extract::Query;
71+
/// use http::Uri;
72+
/// use serde::Deserialize;
73+
///
74+
/// #[derive(Deserialize)]
75+
/// struct ExampleParams {
76+
/// foo: String,
77+
/// bar: u32,
78+
/// }
79+
///
80+
/// let uri: Uri = "http://example.com/path?foo=hello&bar=42".parse().unwrap();
81+
/// let result: Query<ExampleParams> = Query::try_from_uri(&uri).unwrap();
82+
/// assert_eq!(result.foo, String::from("hello"));
83+
/// assert_eq!(result.bar, 42);
84+
/// ```
85+
pub fn try_from_uri(value: &Uri) -> Result<Self, QueryRejection> {
86+
let query = value.query().unwrap_or_default();
87+
let params =
6088
serde_urlencoded::from_str(query).map_err(FailedToDeserializeQueryString::from_err)?;
61-
Ok(Query(value))
89+
Ok(Query(params))
6290
}
6391
}
6492

@@ -137,4 +165,32 @@ mod tests {
137165
let res = client.get("/?n=hi").send().await;
138166
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
139167
}
168+
169+
#[test]
170+
fn test_try_from_uri() {
171+
#[derive(Deserialize)]
172+
struct TestQueryParams {
173+
foo: String,
174+
bar: u32,
175+
}
176+
let uri: Uri = "http://example.com/path?foo=hello&bar=42".parse().unwrap();
177+
let result: Query<TestQueryParams> = Query::try_from_uri(&uri).unwrap();
178+
assert_eq!(result.foo, String::from("hello"));
179+
assert_eq!(result.bar, 42);
180+
}
181+
182+
#[test]
183+
fn test_try_from_uri_with_invalid_query() {
184+
#[derive(Deserialize)]
185+
struct TestQueryParams {
186+
_foo: String,
187+
_bar: u32,
188+
}
189+
let uri: Uri = "http://example.com/path?foo=hello&bar=invalid"
190+
.parse()
191+
.unwrap();
192+
let result: Result<Query<TestQueryParams>, _> = Query::try_from_uri(&uri);
193+
194+
assert!(result.is_err());
195+
}
140196
}

0 commit comments

Comments
 (0)