//! Functionality for using `serde_qs` with `actix_web`. //! //! Enable with the `actix` feature. use actix_web::FromRequest; use actix_web::HttpRequest; use serde::de; use std::ops::{Deref, DerefMut}; use std::rc::Rc; use std::fmt; use error::Error as QsError; #[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from from the request's query. /// `serde_qs` equivalent to `actix_web::Query`. /// /// ## Example /// /// ```rust /// # extern crate actix_web; /// # extern crate serde_qs; /// #[macro_use] extern crate serde_derive; /// use actix_web::{App, http}; /// use serde_qs::actix::QsQuery; /// ///#[derive(Deserialize)] ///pub struct Request { /// id: Vec, ///} /// /// // use `with` extractor for query info /// // this handler get called only if request's query contains `username` field /// // The correct request for this handler would be `/index.html?id[]=1&id[]=2"` /// fn index(info: QsQuery) -> String { /// format!("Request for client with list of ids={:?}", info.id) /// } /// /// fn main() { /// let app = App::new().resource( /// "/index.html", /// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor /// } /// ``` pub struct QsQuery(T); impl Deref for QsQuery { type Target = T; fn deref(&self) -> &T { &self.0 } } impl DerefMut for QsQuery { fn deref_mut(&mut self) -> &mut T { &mut self.0 } } impl QsQuery { /// Deconstruct to a inner value pub fn into_inner(self) -> T { self.0 } } impl FromRequest for QsQuery where T: de::DeserializeOwned, { type Config = QsQueryConfig; type Result = Result; #[inline] fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { let req2 = req.clone(); let err = Rc::clone(&cfg.ehandler); super::from_str::(req.query_string()) .map_err(move |e| (*err)(e, &req2)) .map(QsQuery) } } /// QsQuery extractor configuration /// /// ```rust /// # extern crate actix_web; /// # extern crate serde_qs; /// #[macro_use] extern crate serde_derive; /// use actix_web::{error, http, App, HttpResponse, Result}; /// use serde_qs::actix::QsQuery; /// /// #[derive(Deserialize)] /// struct Info { /// username: String, /// } /// /// /// deserialize `Info` from request's body, max payload size is 4kb /// fn index(info: QsQuery) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// /// fn main() { /// let app = App::new().resource("/index.html", |r| { /// r.method(http::Method::GET).with_config(index, |cfg| { /// cfg.0.error_handler(|err, req| { /// // <- create custom error response /// error::InternalError::from_response(err.description().to_string(), HttpResponse::Conflict().finish()).into() /// }); /// }) /// }); /// } /// ``` pub struct QsQueryConfig { ehandler: Rc) -> actix_web::Error>, } impl QsQueryConfig { /// Set custom error handler pub fn error_handler(&mut self, f: F) -> &mut Self where F: Fn(QsError, &HttpRequest) -> actix_web::Error + 'static, { self.ehandler = Rc::new(f); self } } impl Default for QsQueryConfig { fn default() -> Self { QsQueryConfig { ehandler: Rc::new(|_, _| actix_web::error::UrlencodedError::Parse.into()), } } } impl fmt::Debug for QsQuery { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } } impl fmt::Display for QsQuery { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } }