actix_web_validator/
query.rs

1//! Query extractor.
2use crate::error::Error;
3use std::ops::Deref;
4use std::sync::Arc;
5use std::{fmt, ops};
6
7use actix_web::{FromRequest, HttpRequest};
8use futures::future::{err, ok, Ready};
9use serde::de;
10use validator::Validate;
11
12type ErrHandler = Arc<dyn Fn(Error, &HttpRequest) -> actix_web::Error + Send + Sync>;
13
14/// Query extractor configuration.
15///
16/// ## Example
17///
18/// ```rust
19/// use actix_web::{error, web, App, FromRequest, HttpResponse};
20/// use serde::Deserialize;
21/// use actix_web_validator::{Query, QueryConfig};
22/// use validator::Validate;
23///
24/// #[derive(Deserialize, Validate)]
25/// struct Info {
26///     #[validate(length(min = 1))]
27///     username: String,
28/// }
29///
30/// /// deserialize `Info` from request's querystring
31/// async fn index(info: Query<Info>) -> String {
32///     format!("Welcome {}!", info.username)
33/// }
34///
35/// fn main() {
36///     let query_config = QueryConfig::default()
37///         .error_handler(|err, req| {  // <- create custom error response
38///             error::InternalError::from_response(err, HttpResponse::Conflict().finish()).into()
39///         });
40///     let app = App::new().service(
41///         web::resource("/index.html")
42///             .app_data(query_config)
43///             .route(web::post().to(index))
44///     );
45/// }
46/// ```
47#[derive(Clone, Default)]
48pub struct QueryConfig {
49    pub ehandler: Option<ErrHandler>,
50}
51
52impl QueryConfig {
53    /// Set custom error handler
54    pub fn error_handler<F>(mut self, f: F) -> Self
55    where
56        F: Fn(Error, &HttpRequest) -> actix_web::Error + Send + Sync + 'static,
57    {
58        self.ehandler = Some(Arc::new(f));
59        self
60    }
61}
62
63/// Extract and validate typed information from the request's query.
64///
65/// For query decoding uses *serde_urlencoded* crate
66/// [**QueryConfig**](struct.QueryConfig.html) allows to configure extraction process.
67///
68/// ## Example
69///
70/// ```rust
71/// use actix_web::{web, App};
72/// use serde::Deserialize;
73/// use actix_web_validator::Query;
74/// use validator::Validate;
75///
76/// #[derive(Debug, Deserialize)]
77/// pub enum ResponseType {
78///     Token,
79///     Code
80/// }
81///
82/// #[derive(Deserialize, Validate)]
83/// pub struct AuthRequest {
84///     #[validate(range(min = 1000, max = 9999))]
85///     id: u64,
86///     response_type: ResponseType,
87/// }
88///
89/// // Use `Query` extractor for query information (and destructure it within the signature).
90/// // This handler gets called only if the request's query string contains a `id` and
91/// // `response_type` fields.
92/// // The correct request for this handler would be `/index.html?id=1234&response_type=Code"`.
93/// async fn index(info: Query<AuthRequest>) -> String {
94///     format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type)
95/// }
96///
97/// fn main() {
98///     let app = App::new().service(
99///         web::resource("/index.html").route(web::get().to(index))); // <- use `Query` extractor
100/// }
101/// ```
102#[derive(PartialEq, Eq, PartialOrd, Ord)]
103pub struct Query<T>(pub T);
104
105impl<T> AsRef<T> for Query<T> {
106    fn as_ref(&self) -> &T {
107        &self.0
108    }
109}
110
111impl<T> Deref for Query<T> {
112    type Target = T;
113
114    fn deref(&self) -> &T {
115        &self.0
116    }
117}
118
119impl<T> ops::DerefMut for Query<T> {
120    fn deref_mut(&mut self) -> &mut T {
121        &mut self.0
122    }
123}
124
125impl<T: fmt::Debug> fmt::Debug for Query<T> {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        self.0.fmt(f)
128    }
129}
130
131impl<T: fmt::Display> fmt::Display for Query<T> {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        self.0.fmt(f)
134    }
135}
136
137impl<T> Query<T>
138where
139    T: Validate,
140{
141    /// Deconstruct to an inner value.
142    pub fn into_inner(self) -> T {
143        self.0
144    }
145}
146
147/// Extract typed information from the request's query.
148///
149/// ## Example
150///
151/// ```rust
152/// use actix_web::{web, App};
153/// use serde::Deserialize;
154/// use actix_web_validator::Query;
155/// use validator::Validate;
156///
157/// #[derive(Debug, Deserialize)]
158/// pub enum ResponseType {
159///     Token,
160///     Code
161/// }
162///
163/// #[derive(Deserialize, Validate)]
164/// pub struct AuthRequest {
165///     #[validate(range(min = 1000, max = 9999))]
166///     id: u64,
167///     response_type: ResponseType,
168/// }
169///
170/// // Use `Query` extractor for query information (and destructure it within the signature).
171/// // This handler gets called only if the request's query string contains a `id` and
172/// // `response_type` fields.
173/// // The correct request for this handler would be `/index.html?id=19&response_type=Code"`.
174/// async fn index(web::Query(info): web::Query<AuthRequest>) -> String {
175///     format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type)
176/// }
177///
178/// fn main() {
179///     let app = App::new().service(
180///        web::resource("/index.html").route(web::get().to(index))); // <- use `Query` extractor
181/// }
182/// ```
183impl<T> FromRequest for Query<T>
184where
185    T: de::DeserializeOwned + Validate,
186{
187    type Error = actix_web::Error;
188    type Future = Ready<Result<Self, Self::Error>>;
189
190    /// Builds Query struct from request and provides validation mechanism
191    #[inline]
192    fn from_request(req: &actix_web::HttpRequest, _: &mut actix_web::dev::Payload) -> Self::Future {
193        let error_handler = req
194            .app_data::<QueryConfig>()
195            .map(|c| c.ehandler.clone())
196            .unwrap_or(None);
197
198        serde_urlencoded::from_str::<T>(req.query_string())
199            .map_err(Error::from)
200            .and_then(|value| {
201                value
202                    .validate()
203                    .map(move |_| value)
204                    .map_err(Error::Validate)
205            })
206            .map_err(move |e| {
207                log::debug!(
208                    "Failed during Query extractor validation. \
209                     Request path: {:?}",
210                    req.path()
211                );
212                if let Some(error_handler) = error_handler {
213                    (error_handler)(e, req)
214                } else {
215                    e.into()
216                }
217            })
218            .map(|value| ok(Query(value)))
219            .unwrap_or_else(err)
220    }
221}