11use axum:: {
2- extract:: FromRequestParts ,
2+ extract:: { FromRequestParts , OptionalFromRequestParts } ,
33 response:: { IntoResponse , Response } ,
44 Error ,
55} ;
@@ -18,6 +18,19 @@ use std::fmt;
1818/// with the `multiple` attribute. Those values can be collected into a `Vec` or other sequential
1919/// container.
2020///
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+ ///
2134/// # Example
2235///
2336/// ```rust,no_run
@@ -96,6 +109,27 @@ where
96109 }
97110}
98111
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+
99133axum_core:: __impl_deref!( Query ) ;
100134
101135/// Rejection used for [`Query`].
@@ -182,9 +216,11 @@ impl std::error::Error for QueryRejection {
182216///
183217/// [example]: https://github.com/tokio-rs/axum/blob/main/examples/query-params-with-empty-strings/src/main.rs
184218#[ cfg_attr( docsrs, doc( cfg( feature = "query" ) ) ) ]
219+ #[ deprecated = "Use Option<Query<_>> instead" ]
185220#[ derive( Debug , Clone , Copy , Default ) ]
186221pub struct OptionalQuery < T > ( pub Option < T > ) ;
187222
223+ #[ allow( deprecated) ]
188224impl < T , S > FromRequestParts < S > for OptionalQuery < T >
189225where
190226 T : DeserializeOwned ,
@@ -204,6 +240,7 @@ where
204240 }
205241}
206242
243+ #[ allow( deprecated) ]
207244impl < T > std:: ops:: Deref for OptionalQuery < T > {
208245 type Target = Option < T > ;
209246
@@ -213,6 +250,7 @@ impl<T> std::ops::Deref for OptionalQuery<T> {
213250 }
214251}
215252
253+ #[ allow( deprecated) ]
216254impl < T > std:: ops:: DerefMut for OptionalQuery < T > {
217255 #[ inline]
218256 fn deref_mut ( & mut self ) -> & mut Self :: Target {
@@ -260,6 +298,7 @@ impl std::error::Error for OptionalQueryRejection {
260298}
261299
262300#[ cfg( test) ]
301+ #[ allow( deprecated) ]
263302mod tests {
264303 use super :: * ;
265304 use crate :: test_helpers:: * ;
0 commit comments