1use std::{fmt, ops, sync::Arc};
4
5#[expect(deprecated, reason = "preserve the public ready future type")]
6use actix_utils::future::{ready, Ready};
7use serde::de::DeserializeOwned;
8
9use crate::{dev::Payload, error::QueryPayloadError, Error, FromRequest, HttpRequest};
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
62pub struct Query<T>(pub T);
63
64impl<T> Query<T> {
65 pub fn into_inner(self) -> T {
67 self.0
68 }
69}
70
71impl<T: DeserializeOwned> Query<T> {
72 pub fn from_query(query_str: &str) -> Result<Self, QueryPayloadError> {
83 serde_urlencoded::from_str::<T>(query_str)
84 .map(Self)
85 .map_err(QueryPayloadError::Deserialize)
86 }
87}
88
89impl<T> ops::Deref for Query<T> {
90 type Target = T;
91
92 fn deref(&self) -> &T {
93 &self.0
94 }
95}
96
97impl<T> ops::DerefMut for Query<T> {
98 fn deref_mut(&mut self) -> &mut T {
99 &mut self.0
100 }
101}
102
103impl<T: fmt::Display> fmt::Display for Query<T> {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 self.0.fmt(f)
106 }
107}
108
109#[expect(deprecated, reason = "preserve the public ready future type")]
111impl<T: DeserializeOwned> FromRequest for Query<T> {
112 type Error = Error;
113 type Future = Ready<Result<Self, Error>>;
114
115 #[inline]
116 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
117 let error_handler = req
118 .app_data::<QueryConfig>()
119 .and_then(|c| c.err_handler.clone());
120
121 serde_urlencoded::from_str::<T>(req.query_string())
122 .map(|val| ready(Ok(Query(val))))
123 .unwrap_or_else(move |err| {
124 let err = QueryPayloadError::Deserialize(err);
125
126 log::debug!(
127 "Failed during Query extractor deserialization. \
128 Request path: {:?}",
129 req.path()
130 );
131
132 let err = if let Some(error_handler) = error_handler {
133 (error_handler)(err, req)
134 } else {
135 err.into()
136 };
137
138 ready(Err(err))
139 })
140 }
141}
142
143#[derive(Clone, Default)]
173pub struct QueryConfig {
174 #[allow(clippy::type_complexity)]
175 err_handler: Option<Arc<dyn Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync>>,
176}
177
178impl QueryConfig {
179 pub fn error_handler<F>(mut self, f: F) -> Self
181 where
182 F: Fn(QueryPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,
183 {
184 self.err_handler = Some(Arc::new(f));
185 self
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use actix_http::StatusCode;
192 use derive_more::Display;
193 use serde::Deserialize;
194
195 use super::*;
196 use crate::{error::InternalError, test::TestRequest, HttpResponse};
197
198 #[derive(Deserialize, Debug, Display)]
199 struct Id {
200 id: String,
201 }
202
203 #[actix_rt::test]
204 async fn test_service_request_extract() {
205 let req = TestRequest::with_uri("/name/user1/").to_srv_request();
206 assert!(Query::<Id>::from_query(req.query_string()).is_err());
207
208 let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
209 let mut s = Query::<Id>::from_query(req.query_string()).unwrap();
210
211 assert_eq!(s.id, "test");
212 assert_eq!(
213 format!("{}, {:?}", s, s),
214 "test, Query(Id { id: \"test\" })"
215 );
216
217 s.id = "test1".to_string();
218 let s = s.into_inner();
219 assert_eq!(s.id, "test1");
220 }
221
222 #[actix_rt::test]
223 async fn test_request_extract() {
224 let req = TestRequest::with_uri("/name/user1/").to_srv_request();
225 let (req, mut pl) = req.into_parts();
226 assert!(Query::<Id>::from_request(&req, &mut pl).await.is_err());
227
228 let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
229 let (req, mut pl) = req.into_parts();
230
231 let mut s = Query::<Id>::from_request(&req, &mut pl).await.unwrap();
232 assert_eq!(s.id, "test");
233 assert_eq!(
234 format!("{}, {:?}", s, s),
235 "test, Query(Id { id: \"test\" })"
236 );
237
238 s.id = "test1".to_string();
239 let s = s.into_inner();
240 assert_eq!(s.id, "test1");
241 }
242
243 #[actix_rt::test]
244 #[should_panic]
245 async fn test_tuple_panic() {
246 let req = TestRequest::with_uri("/?one=1&two=2").to_srv_request();
247 let (req, mut pl) = req.into_parts();
248
249 Query::<(u32, u32)>::from_request(&req, &mut pl)
250 .await
251 .unwrap();
252 }
253
254 #[actix_rt::test]
255 async fn test_custom_error_responder() {
256 let req = TestRequest::with_uri("/name/user1/")
257 .app_data(QueryConfig::default().error_handler(|e, _| {
258 let resp = HttpResponse::UnprocessableEntity().finish();
259 InternalError::from_response(e, resp).into()
260 }))
261 .to_srv_request();
262
263 let (req, mut pl) = req.into_parts();
264 let query = Query::<Id>::from_request(&req, &mut pl).await;
265
266 assert!(query.is_err());
267 assert_eq!(
268 query
269 .unwrap_err()
270 .as_response_error()
271 .error_response()
272 .status(),
273 StatusCode::UNPROCESSABLE_ENTITY
274 );
275 }
276}