From 30c3802305fab702c1704091013c5af874997c01 Mon Sep 17 00:00:00 2001 From: Sam Scott Date: Wed, 23 Jan 2019 12:06:41 -0500 Subject: Add feature for easy use in `actix_web` extractors. --- src/actix.rs | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 15 ++++++ 2 files changed, 163 insertions(+) create mode 100644 src/actix.rs (limited to 'src') diff --git a/src/actix.rs b/src/actix.rs new file mode 100644 index 0000000..06cafc6 --- /dev/null +++ b/src/actix.rs @@ -0,0 +1,148 @@ +//! 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) + } +} diff --git a/src/lib.rs b/src/lib.rs index 53bfef2..b08c49c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,6 +146,17 @@ //! assert_eq!(rec_query.unwrap(), query); //! } //! ``` +//! +//! ## Use with `actix_web` extractors +//! +//! The `actix` feature enables the use of `serde_qs::actix::QsQuery`, which +//! is a direct substitute for the `actix_web::Query` and can be used as an extractor: +//! +//! ```ignore +//! fn index(info: QsQuery) -> Result { +//! Ok(format!("Welcome {}!", info.username)) +//! } +//! ``` #![allow()] #![deny( @@ -192,6 +203,8 @@ while_true )] +#[cfg(feature = "actix")] +extern crate actix_web; extern crate data_encoding; #[macro_use] extern crate error_chain; @@ -199,6 +212,8 @@ extern crate percent_encoding; #[macro_use] extern crate serde; +#[cfg(feature = "actix")] +pub mod actix; mod de; mod error; mod ser; -- cgit v1.2.3