1
1
use axum:: {
2
- extract:: FromRequestParts ,
2
+ extract:: { FromRequestParts , OptionalFromRequestParts } ,
3
3
response:: { IntoResponse , Response } ,
4
4
Error ,
5
5
} ;
@@ -18,6 +18,19 @@ use std::fmt;
18
18
/// with the `multiple` attribute. Those values can be collected into a `Vec` or other sequential
19
19
/// container.
20
20
///
21
+ /// # `Option<Query<T>>` behavior
22
+ ///
23
+ /// If `Query<T>` itself is used as an extractor and there is no query string in
24
+ /// the request URL, `T`'s `Deserialize` implementation is called on an empty
25
+ /// string instead.
26
+ ///
27
+ /// You can avoid this by using `Option<Query<T>>`, which gives you `None` in
28
+ /// the case that there is no query string in the request URL.
29
+ ///
30
+ /// Note that an empty query string is not the same as no query string, that is
31
+ /// `https://example.org/` and `https://example.org/?` are not treated the same
32
+ /// in this case.
33
+ ///
21
34
/// # Example
22
35
///
23
36
/// ```rust,no_run
@@ -96,6 +109,27 @@ where
96
109
}
97
110
}
98
111
112
+ impl < T , S > OptionalFromRequestParts < S > for Query < T >
113
+ where
114
+ T : DeserializeOwned ,
115
+ S : Send + Sync ,
116
+ {
117
+ type Rejection = QueryRejection ;
118
+
119
+ async fn from_request_parts (
120
+ parts : & mut Parts ,
121
+ _state : & S ,
122
+ ) -> Result < Option < Self > , Self :: Rejection > {
123
+ if let Some ( query) = parts. uri . query ( ) {
124
+ let value = serde_html_form:: from_str ( query)
125
+ . map_err ( |err| QueryRejection :: FailedToDeserializeQueryString ( Error :: new ( err) ) ) ?;
126
+ Ok ( Some ( Self ( value) ) )
127
+ } else {
128
+ Ok ( None )
129
+ }
130
+ }
131
+ }
132
+
99
133
axum_core:: __impl_deref!( Query ) ;
100
134
101
135
/// Rejection used for [`Query`].
@@ -182,9 +216,11 @@ impl std::error::Error for QueryRejection {
182
216
///
183
217
/// [example]: https://github.com/tokio-rs/axum/blob/main/examples/query-params-with-empty-strings/src/main.rs
184
218
#[ cfg_attr( docsrs, doc( cfg( feature = "query" ) ) ) ]
219
+ #[ deprecated = "Use Option<Query<_>> instead" ]
185
220
#[ derive( Debug , Clone , Copy , Default ) ]
186
221
pub struct OptionalQuery < T > ( pub Option < T > ) ;
187
222
223
+ #[ allow( deprecated) ]
188
224
impl < T , S > FromRequestParts < S > for OptionalQuery < T >
189
225
where
190
226
T : DeserializeOwned ,
@@ -204,6 +240,7 @@ where
204
240
}
205
241
}
206
242
243
+ #[ allow( deprecated) ]
207
244
impl < T > std:: ops:: Deref for OptionalQuery < T > {
208
245
type Target = Option < T > ;
209
246
@@ -213,6 +250,7 @@ impl<T> std::ops::Deref for OptionalQuery<T> {
213
250
}
214
251
}
215
252
253
+ #[ allow( deprecated) ]
216
254
impl < T > std:: ops:: DerefMut for OptionalQuery < T > {
217
255
#[ inline]
218
256
fn deref_mut ( & mut self ) -> & mut Self :: Target {
@@ -260,6 +298,7 @@ impl std::error::Error for OptionalQueryRejection {
260
298
}
261
299
262
300
#[ cfg( test) ]
301
+ #[ allow( deprecated) ]
263
302
mod tests {
264
303
use super :: * ;
265
304
use crate :: test_helpers:: * ;
0 commit comments