serde_querystring_actix/
lib.rs1#![doc = include_str!("../README.md")]
2
3use std::future::{ready, Ready};
4use std::sync::Arc;
5use std::{fmt, ops};
6
7use actix_web::dev::Payload;
8use actix_web::http::StatusCode;
9use actix_web::{Error, FromRequest, HttpRequest, ResponseError};
10use derive_more::{Display, From};
11use serde::de;
12
13pub use serde_querystring::de::ParseMode;
14
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
52pub struct QueryString<T>(pub T);
53
54impl<T> QueryString<T> {
55 pub fn into_inner(self) -> T {
57 self.0
58 }
59
60 pub fn from_query(
62 query_str: &str,
63 parse_mode: serde_querystring::de::ParseMode,
64 ) -> Result<Self, QueryStringPayloadError>
65 where
66 T: de::DeserializeOwned,
67 {
68 serde_querystring::de::from_str::<T>(query_str, parse_mode)
69 .map(Self)
70 .map_err(QueryStringPayloadError::Deserialize)
71 }
72}
73
74impl<T> ops::Deref for QueryString<T> {
75 type Target = T;
76
77 fn deref(&self) -> &T {
78 &self.0
79 }
80}
81
82impl<T> ops::DerefMut for QueryString<T> {
83 fn deref_mut(&mut self) -> &mut T {
84 &mut self.0
85 }
86}
87
88impl<T: fmt::Display> fmt::Display for QueryString<T> {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 self.0.fmt(f)
91 }
92}
93
94impl<T> FromRequest for QueryString<T>
95where
96 T: de::DeserializeOwned,
97{
98 type Error = Error;
99 type Future = Ready<Result<Self, Error>>;
100
101 #[inline]
102 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
103 let config = req
104 .app_data::<QueryStringConfig>()
105 .cloned()
106 .unwrap_or_default();
107
108 serde_querystring::de::from_str::<T>(req.query_string(), config.mode)
109 .map(|val| ready(Ok(QueryString(val))))
110 .unwrap_or_else(move |e| {
111 let e = QueryStringPayloadError::Deserialize(e);
112
113 log::debug!(
114 "Failed during QueryString extractor deserialization. \
115 Request path: {:?}",
116 req.path()
117 );
118
119 let e = if let Some(error_handler) = config.ehandler {
120 (error_handler)(e, req)
121 } else {
122 e.into()
123 };
124
125 ready(Err(e))
126 })
127 }
128}
129
130#[derive(Clone)]
165pub struct QueryStringConfig {
166 mode: serde_querystring::de::ParseMode,
167 ehandler: Option<Arc<dyn Fn(QueryStringPayloadError, &HttpRequest) -> Error + Send + Sync>>,
168}
169
170impl QueryStringConfig {
171 pub fn error_handler<F>(mut self, f: F) -> Self
173 where
174 F: Fn(QueryStringPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,
175 {
176 self.ehandler = Some(Arc::new(f));
177 self
178 }
179
180 pub fn parse_mode(mut self, mode: serde_querystring::de::ParseMode) -> Self {
181 self.mode = mode;
182 self
183 }
184}
185
186impl Default for QueryStringConfig {
187 fn default() -> Self {
188 QueryStringConfig {
189 mode: serde_querystring::de::ParseMode::Duplicate,
190 ehandler: None,
191 }
192 }
193}
194
195#[derive(Debug, Display, From)]
197pub enum QueryStringPayloadError {
198 #[display(fmt = "Query deserialize error: {}", _0)]
200 Deserialize(serde_querystring::de::Error),
201}
202
203impl std::error::Error for QueryStringPayloadError {}
204
205impl ResponseError for QueryStringPayloadError {
207 fn status_code(&self) -> StatusCode {
208 StatusCode::BAD_REQUEST
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use actix_web::error::InternalError;
215 use actix_web::http::StatusCode;
216 use actix_web::test::TestRequest;
217 use actix_web::HttpResponse;
218 use derive_more::Display;
219 use serde::Deserialize;
220
221 use super::*;
222
223 #[derive(Deserialize, Debug, Display)]
224 struct Id {
225 id: String,
226 }
227
228 #[actix_rt::test]
229 async fn test_service_request_extract() {
230 let req = TestRequest::with_uri("/name/user1/").to_srv_request();
231 assert!(QueryString::<Id>::from_query(
232 &req.query_string(),
233 serde_querystring::de::ParseMode::UrlEncoded
234 )
235 .is_err());
236
237 let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
238 let mut s = QueryString::<Id>::from_query(
239 &req.query_string(),
240 serde_querystring::de::ParseMode::UrlEncoded,
241 )
242 .unwrap();
243
244 assert_eq!(s.id, "test");
245 assert_eq!(
246 format!("{}, {:?}", s, s),
247 "test, QueryString(Id { id: \"test\" })"
248 );
249
250 s.id = "test1".to_string();
251 let s = s.into_inner();
252 assert_eq!(s.id, "test1");
253 }
254
255 #[actix_rt::test]
256 async fn test_request_extract() {
257 let req = TestRequest::with_uri("/name/user1/").to_srv_request();
258 let (req, mut pl) = req.into_parts();
259 assert!(QueryString::<Id>::from_request(&req, &mut pl)
260 .await
261 .is_err());
262
263 let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
264 let (req, mut pl) = req.into_parts();
265
266 let mut s = QueryString::<Id>::from_request(&req, &mut pl)
267 .await
268 .unwrap();
269 assert_eq!(s.id, "test");
270 assert_eq!(
271 format!("{}, {:?}", s, s),
272 "test, QueryString(Id { id: \"test\" })"
273 );
274
275 s.id = "test1".to_string();
276 let s = s.into_inner();
277 assert_eq!(s.id, "test1");
278 }
279
280 #[actix_rt::test]
281 async fn test_custom_error_responder() {
282 let req = TestRequest::with_uri("/name/user1/")
283 .app_data(QueryStringConfig::default().error_handler(|e, _| {
284 let resp = HttpResponse::UnprocessableEntity().finish();
285 InternalError::from_response(e, resp).into()
286 }))
287 .to_srv_request();
288
289 let (req, mut pl) = req.into_parts();
290 let query = QueryString::<Id>::from_request(&req, &mut pl).await;
291
292 assert!(query.is_err());
293 assert_eq!(
294 query
295 .unwrap_err()
296 .as_response_error()
297 .error_response()
298 .status(),
299 StatusCode::UNPROCESSABLE_ENTITY
300 );
301 }
302}