diff options
author | Sam Scott <sam.scott89@gmail.com> | 2019-01-23 12:06:41 -0500 |
---|---|---|
committer | Sam Scott <sam.scott89@gmail.com> | 2019-01-23 12:06:41 -0500 |
commit | 30c3802305fab702c1704091013c5af874997c01 (patch) | |
tree | 41bd13460f5752caed77ec459efe9264b1629e24 /src | |
parent | 3fe8fc3dc024cf46c25223ce3f8fb7bcbfb15547 (diff) |
Add feature for easy use in `actix_web` extractors.
Diffstat (limited to 'src')
-rw-r--r-- | src/actix.rs | 148 | ||||
-rw-r--r-- | src/lib.rs | 15 |
2 files changed, 163 insertions, 0 deletions
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<u64>,
+///}
+///
+/// // 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<Request>) -> 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>(T);
+
+impl<T> Deref for QsQuery<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.0
+ }
+}
+
+impl<T> DerefMut for QsQuery<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.0
+ }
+}
+
+impl<T> QsQuery<T> {
+ /// Deconstruct to a inner value
+ pub fn into_inner(self) -> T {
+ self.0
+ }
+}
+
+impl<T, S> FromRequest<S> for QsQuery<T>
+where
+ T: de::DeserializeOwned,
+{
+ type Config = QsQueryConfig<S>;
+ type Result = Result<Self, actix_web::Error>;
+
+ #[inline]
+ fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
+ let req2 = req.clone();
+ let err = Rc::clone(&cfg.ehandler);
+ super::from_str::<T>(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<Info>) -> Result<String> {
+/// 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<S> {
+ ehandler: Rc<Fn(QsError, &HttpRequest<S>) -> actix_web::Error>,
+}
+impl<S> QsQueryConfig<S> {
+ /// Set custom error handler
+ pub fn error_handler<F>(&mut self, f: F) -> &mut Self
+ where
+ F: Fn(QsError, &HttpRequest<S>) -> actix_web::Error + 'static,
+ {
+ self.ehandler = Rc::new(f);
+ self
+ }
+}
+
+impl<S> Default for QsQueryConfig<S> {
+ fn default() -> Self {
+ QsQueryConfig {
+ ehandler: Rc::new(|_, _| actix_web::error::UrlencodedError::Parse.into()),
+ }
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for QsQuery<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl<T: fmt::Display> fmt::Display for QsQuery<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
@@ -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<Info>) -> Result<String> { +//! 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; |