1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-01-31 11:02:08 +01:00

added State extractor

This commit is contained in:
Nikolay Kim 2018-03-29 15:41:13 -07:00
parent d24752d9bc
commit 3e98177fad
4 changed files with 90 additions and 76 deletions

View File

@ -33,7 +33,7 @@ Actix web is a simple, pragmatic, extremely fast, web framework for Rust.
```rust ```rust
extern crate actix_web; extern crate actix_web;
use actix_web::*; use actix_web::{Application, HttpServer, Path};
fn index(info: Path<(String, u32)>) -> String { fn index(info: Path<(String, u32)>) -> String {
format!("Hello {}! id:{}", info.0, info.1) format!("Hello {}! id:{}", info.0, info.1)

105
src/de.rs
View File

@ -12,8 +12,6 @@ use httprequest::HttpRequest;
/// Extract typed information from the request's path. /// Extract typed information from the request's path.
/// ///
/// `S` - application state type
///
/// ## Example /// ## Example
/// ///
/// ```rust /// ```rust
@ -49,66 +47,48 @@ use httprequest::HttpRequest;
/// # use actix_web::*; /// # use actix_web::*;
/// use actix_web::Path; /// use actix_web::Path;
/// ///
/// /// Application state
/// struct State {}
///
/// #[derive(Deserialize)] /// #[derive(Deserialize)]
/// struct Info { /// struct Info {
/// username: String, /// username: String,
/// } /// }
/// ///
/// /// extract path info using serde /// /// extract path info using serde
/// fn index(info: Path<Info, State>) -> Result<String> { /// fn index(info: Path<Info>) -> Result<String> {
/// Ok(format!("Welcome {}!", info.username)) /// Ok(format!("Welcome {}!", info.username))
/// } /// }
/// ///
/// fn main() { /// fn main() {
/// let app = Application::with_state(State{}).resource( /// let app = Application::new().resource(
/// "/{username}/index.html", // <- define path parameters /// "/{username}/index.html", // <- define path parameters
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor /// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// } /// }
/// ``` /// ```
pub struct Path<T, S=()>{ pub struct Path<T>{
item: T, inner: T
req: HttpRequest<S>,
} }
impl<T, S> Deref for Path<T, S> { impl<T> Deref for Path<T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
&self.item &self.inner
} }
} }
impl<T, S> DerefMut for Path<T, S> { impl<T> DerefMut for Path<T> {
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
&mut self.item &mut self.inner
} }
} }
impl<T, S> Path<T, S> { impl<T> Path<T> {
/// Deconstruct to a inner value
/// Shared application state pub fn into_inner(self) -> T {
#[inline] self.inner
pub fn state(&self) -> &S {
self.req.state()
} }
/// Incoming request
#[inline]
pub fn request(&self) -> &HttpRequest<S> {
&self.req
}
/// Deconstruct instance into parts
pub fn into(self) -> (T, HttpRequest<S>) {
(self.item, self.req)
}
} }
impl<T, S> FromRequest<S> for Path<T, S> impl<T, S> FromRequest<S> for Path<T>
where T: DeserializeOwned, S: 'static where T: DeserializeOwned, S: 'static
{ {
type Result = FutureResult<Self, Error>; type Result = FutureResult<Self, Error>;
@ -118,14 +98,12 @@ impl<T, S> FromRequest<S> for Path<T, S>
let req = req.clone(); let req = req.clone();
result(de::Deserialize::deserialize(PathDeserializer{req: &req}) result(de::Deserialize::deserialize(PathDeserializer{req: &req})
.map_err(|e| e.into()) .map_err(|e| e.into())
.map(|item| Path{item, req})) .map(|inner| Path{inner}))
} }
} }
/// Extract typed information from from the request's query. /// Extract typed information from from the request's query.
/// ///
/// `S` - application state type
///
/// ## Example /// ## Example
/// ///
/// ```rust /// ```rust
@ -136,9 +114,6 @@ impl<T, S> FromRequest<S> for Path<T, S>
/// # use actix_web::*; /// # use actix_web::*;
/// use actix_web::Query; /// use actix_web::Query;
/// ///
/// /// Application state
/// struct State {}
///
/// #[derive(Deserialize)] /// #[derive(Deserialize)]
/// struct Info { /// struct Info {
/// username: String, /// username: String,
@ -146,56 +121,40 @@ impl<T, S> FromRequest<S> for Path<T, S>
/// ///
/// // use `with` extractor for query info /// // use `with` extractor for query info
/// // this handler get called only if request's query contains `username` field /// // this handler get called only if request's query contains `username` field
/// fn index(info: Query<Info, State>) -> Result<String> { /// fn index(info: Query<Info>) -> Result<String> {
/// Ok(format!("Welcome {}!", info.username)) /// Ok(format!("Welcome {}!", info.username))
/// } /// }
/// ///
/// fn main() { /// fn main() {
/// let app = Application::with_state(State{}).resource( /// let app = Application::new().resource(
/// "/index.html", /// "/index.html",
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor /// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// } /// }
/// ``` /// ```
pub struct Query<T, S=()>{ pub struct Query<T>(T);
item: T,
req: HttpRequest<S>,
}
impl<T, S> Deref for Query<T, S> { impl<T> Deref for Query<T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
&self.item &self.0
} }
} }
impl<T, S> DerefMut for Query<T, S> { impl<T> DerefMut for Query<T> {
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
&mut self.item &mut self.0
} }
} }
impl<T, S> Query<T, S> { impl<T> Query<T> {
/// Deconstruct to a inner value
/// Shared application state pub fn into_inner(self) -> T {
#[inline] self.0
pub fn state(&self) -> &S {
self.req.state()
}
/// Incoming request
#[inline]
pub fn request(&self) -> &HttpRequest<S> {
&self.req
}
/// Deconstruct instance into parts
pub fn into(self) -> (T, HttpRequest<S>) {
(self.item, self.req)
} }
} }
impl<T, S> FromRequest<S> for Query<T, S> impl<T, S> FromRequest<S> for Query<T>
where T: de::DeserializeOwned, S: 'static where T: de::DeserializeOwned, S: 'static
{ {
type Result = FutureResult<Self, Error>; type Result = FutureResult<Self, Error>;
@ -205,7 +164,7 @@ impl<T, S> FromRequest<S> for Query<T, S>
let req = req.clone(); let req = req.clone();
result(serde_urlencoded::from_str::<T>(req.query_string()) result(serde_urlencoded::from_str::<T>(req.query_string())
.map_err(|e| e.into()) .map_err(|e| e.into())
.map(|item| Query{ item, req})) .map(Query))
} }
} }
@ -581,7 +540,7 @@ mod tests {
let (router, _) = Router::new("", ServerSettings::default(), routes); let (router, _) = Router::new("", ServerSettings::default(), routes);
assert!(router.recognize(&mut req).is_some()); assert!(router.recognize(&mut req).is_some());
match Path::<MyStruct, _>::from_request(&req).poll().unwrap() { match Path::<MyStruct>::from_request(&req).poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.key, "name"); assert_eq!(s.key, "name");
assert_eq!(s.value, "user1"); assert_eq!(s.value, "user1");
@ -589,7 +548,7 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
match Path::<(String, String), _>::from_request(&req).poll().unwrap() { match Path::<(String, String)>::from_request(&req).poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");
assert_eq!(s.1, "user1"); assert_eq!(s.1, "user1");
@ -597,7 +556,7 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
match Query::<Id, _>::from_request(&req).poll().unwrap() { match Query::<Id>::from_request(&req).poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.id, "test"); assert_eq!(s.id, "test");
}, },
@ -607,7 +566,7 @@ mod tests {
let mut req = TestRequest::with_uri("/name/32/").finish(); let mut req = TestRequest::with_uri("/name/32/").finish();
assert!(router.recognize(&mut req).is_some()); assert!(router.recognize(&mut req).is_some());
match Path::<Test2, _>::from_request(&req).poll().unwrap() { match Path::<Test2>::from_request(&req).poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.key, "name"); assert_eq!(s.key, "name");
assert_eq!(s.value, 32); assert_eq!(s.value, 32);
@ -615,7 +574,7 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
match Path::<(String, u8), _>::from_request(&req).poll().unwrap() { match Path::<(String, u8)>::from_request(&req).poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
assert_eq!(s.0, "name"); assert_eq!(s.0, "name");
assert_eq!(s.1, 32); assert_eq!(s.1, 32);

View File

@ -1,5 +1,6 @@
use std::ops::Deref;
use std::marker::PhantomData; use std::marker::PhantomData;
use futures::future::{Future, ok, err}; use futures::future::{Future, FutureResult, ok, err};
use error::Error; use error::Error;
use httprequest::HttpRequest; use httprequest::HttpRequest;
@ -347,3 +348,57 @@ impl<S, H, F, R, E> RouteHandler<S> for AsyncHandler<S, H, F, R, E>
Reply::async(fut) Reply::async(fut)
} }
} }
/// Access to an application state
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// # use actix_web::*;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::State;
///
/// /// Application state
/// struct App {msg: &'static str}
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// /// extract path info using serde
/// fn index(state: State<App>, info: Path<Info>) -> Result<String> {
/// Ok(format!("{} {}!", state.msg, info.username))
/// }
///
/// fn main() {
/// let app = Application::with_state(App{msg: "Welcome"}).resource(
/// "/{username}/index.html", // <- define path parameters
/// |r| r.method(Method::GET).with2(index)); // <- use `with` extractor
/// }
/// ```
pub struct State<S> (HttpRequest<S>);
impl<S> Deref for State<S> {
type Target = S;
fn deref(&self) -> &S {
self.0.state()
}
}
impl<S: 'static> FromRequest<S> for State<S>
{
type Result = FutureResult<Self, Error>;
#[inline]
fn from_request(req: &HttpRequest<S>) -> Self::Result {
ok(State(req.clone()))
}
}

View File

@ -141,7 +141,7 @@ pub use application::Application;
pub use httpmessage::HttpMessage; pub use httpmessage::HttpMessage;
pub use httprequest::HttpRequest; pub use httprequest::HttpRequest;
pub use httpresponse::HttpResponse; pub use httpresponse::HttpResponse;
pub use handler::{Either, Responder, AsyncResponder, FutureResponse}; pub use handler::{Either, Responder, AsyncResponder, FutureResponse, State};
pub use context::HttpContext; pub use context::HttpContext;
pub use server::HttpServer; pub use server::HttpServer;