1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 07:53:00 +01:00

refactor WithHandler trait

This commit is contained in:
Nikolay Kim 2018-03-27 20:33:24 -07:00
parent 62fb75ff95
commit 4358da9926
8 changed files with 103 additions and 49 deletions

View File

@ -31,7 +31,7 @@ fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
} }
/// This handler uses `With` helper for loading serde json object. /// This handler uses `With` helper for loading serde json object.
fn extract_item(_: HttpRequest, item: Json<MyObj>) -> Result<HttpResponse> { fn extract_item(item: Json<MyObj>) -> Result<HttpResponse> {
println!("model: {:?}", &item); println!("model: {:?}", &item);
httpcodes::HTTPOk.build().json(item.0) // <- send response httpcodes::HTTPOk.build().json(item.0) // <- send response
} }

View File

@ -8,11 +8,11 @@ use error::Error;
use httprequest::HttpRequest; use httprequest::HttpRequest;
pub trait HttpRequestExtractor<T>: Sized where T: DeserializeOwned pub trait HttpRequestExtractor<T, S>: Sized where T: DeserializeOwned, S: 'static
{ {
type Result: Future<Item=Self, Error=Error>; type Result: Future<Item=Self, Error=Error>;
fn extract<S: 'static>(req: &HttpRequest<S>) -> Self::Result; fn extract(req: &HttpRequest<S>) -> Self::Result;
} }
/// Extract typed information from the request's path. /// Extract typed information from the request's path.
@ -33,7 +33,7 @@ pub trait HttpRequestExtractor<T>: Sized where T: DeserializeOwned
/// } /// }
/// ///
/// /// extract path info using serde /// /// extract path info using serde
/// fn index(req: HttpRequest, info: Path<Info>) -> Result<String> { /// fn index(info: Path<Info>) -> Result<String> {
/// Ok(format!("Welcome {}!", info.username)) /// Ok(format!("Welcome {}!", info.username))
/// } /// }
/// ///
@ -43,31 +43,57 @@ pub trait HttpRequestExtractor<T>: Sized where T: DeserializeOwned
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor /// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// } /// }
/// ``` /// ```
pub struct Path<T>(pub T); pub struct Path<T, S=()>{
item: T,
req: HttpRequest<S>,
}
impl<T> Deref for Path<T> { impl<T, S> Deref for Path<T, S> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
&self.0 &self.item
} }
} }
impl<T> DerefMut for Path<T> { impl<T, S> DerefMut for Path<T, S> {
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
&mut self.0 &mut self.item
} }
} }
impl<T> HttpRequestExtractor<T> for Path<T> where T: DeserializeOwned impl<T, S> Path<T, S> {
/// Shared application state
#[inline]
pub fn state(&self) -> &S {
self.req.state()
}
/// Incoming request
#[inline]
pub fn request(&self) -> &HttpRequest<S> {
&self.req
}
/// Deconstruct instance into a parts
pub fn into(self) -> (T, HttpRequest<S>) {
(self.item, self.req)
}
}
impl<T, S> HttpRequestExtractor<T, S> for Path<T, S>
where T: DeserializeOwned, S: 'static
{ {
type Result = FutureResult<Self, Error>; type Result = FutureResult<Self, Error>;
#[inline] #[inline]
fn extract<S: 'static>(req: &HttpRequest<S>) -> Self::Result { fn extract(req: &HttpRequest<S>) -> Self::Result {
result(de::Deserialize::deserialize(PathExtractor{req}) let req = req.clone();
result(de::Deserialize::deserialize(PathExtractor{req: &req})
.map_err(|e| e.into()) .map_err(|e| e.into())
.map(Path)) .map(|item| Path{item, req}))
} }
} }
@ -90,7 +116,7 @@ impl<T> HttpRequestExtractor<T> for Path<T> where T: DeserializeOwned
/// ///
/// // 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(req: HttpRequest, info: Query<Info>) -> Result<String> { /// fn index(info: Query<Info>) -> Result<String> {
/// Ok(format!("Welcome {}!", info.username)) /// Ok(format!("Welcome {}!", info.username))
/// } /// }
/// ///
@ -100,31 +126,56 @@ impl<T> HttpRequestExtractor<T> for Path<T> where T: DeserializeOwned
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor /// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// } /// }
/// ``` /// ```
pub struct Query<T>(pub T); pub struct Query<T, S=()>{
item: T,
req: HttpRequest<S>,
}
impl<T> Deref for Query<T> { impl<T, S> Deref for Query<T, S> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
&self.0 &self.item
} }
} }
impl<T> DerefMut for Query<T> { impl<T, S> DerefMut for Query<T, S> {
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
&mut self.0 &mut self.item
} }
} }
impl<T> HttpRequestExtractor<T> for Query<T> where T: de::DeserializeOwned impl<T, S> Query<T, S> {
/// Shared application state
#[inline]
pub fn state(&self) -> &S {
self.req.state()
}
/// Incoming request
#[inline]
pub fn request(&self) -> &HttpRequest<S> {
&self.req
}
/// Deconstruct instance into a parts
pub fn into(self) -> (T, HttpRequest<S>) {
(self.item, self.req)
}
}
impl<T, S> HttpRequestExtractor<T, S> for Query<T, S>
where T: de::DeserializeOwned, S: 'static
{ {
type Result = FutureResult<Self, Error>; type Result = FutureResult<Self, Error>;
#[inline] #[inline]
fn extract<S: 'static>(req: &HttpRequest<S>) -> Self::Result { fn extract(req: &HttpRequest<S>) -> Self::Result {
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(Query)) .map(|item| Query{ item, req}))
} }
} }

View File

@ -136,7 +136,7 @@ pub trait HttpMessage {
MessageBody::new(self) MessageBody::new(self)
} }
/// Parse `application/x-www-form-urlencoded` encoded body. /// Parse `application/x-www-form-urlencoded` encoded request's body.
/// Return `UrlEncoded` future. It resolves to a `HashMap<String, String>` which /// Return `UrlEncoded` future. It resolves to a `HashMap<String, String>` which
/// contains decoded parameters. /// contains decoded parameters.
/// ///
@ -151,15 +151,15 @@ pub trait HttpMessage {
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;
/// # use futures::Future;
/// use actix_web::*; /// use actix_web::*;
/// use futures::future::{Future, ok};
/// ///
/// fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> { /// fn index(mut req: HttpRequest) -> FutureResponse<HttpResponse> {
/// req.urlencoded() // <- get UrlEncoded future /// req.urlencoded() // <- get UrlEncoded future
/// .from_err() /// .from_err()
/// .and_then(|params| { // <- url encoded parameters /// .and_then(|params| { // <- url encoded parameters
/// println!("==== BODY ==== {:?}", params); /// println!("==== BODY ==== {:?}", params);
/// ok(httpcodes::HttpOk.into()) /// Ok(httpcodes::HttpOk.into())
/// }) /// })
/// .responder() /// .responder()
/// } /// }

View File

@ -430,8 +430,8 @@ impl<S> HttpRequest<S> {
where S: 'static, where S: 'static,
T: de::DeserializeOwned, T: de::DeserializeOwned,
{ {
match Path::<T>::extract(self).poll()? { match Path::<T, _>::extract(self).poll()? {
Async::Ready(val) => Ok(val.0), Async::Ready(val) => Ok(val.into().0),
_ => unreachable!() _ => unreachable!()
} }
} }
@ -482,8 +482,8 @@ impl<S> HttpRequest<S> {
where S: 'static, where S: 'static,
T: de::DeserializeOwned, T: de::DeserializeOwned,
{ {
match Query::<T>::extract(self).poll()? { match Query::<T, _>::extract(self).poll()? {
Async::Ready(val) => Ok(val.0), Async::Ready(val) => Ok(val.into().0),
_ => unreachable!() _ => unreachable!()
} }
} }

View File

@ -63,12 +63,13 @@ impl<T: Serialize> Responder for Json<T> {
} }
} }
impl<T> HttpRequestExtractor<T> for Json<T> where T: DeserializeOwned + 'static impl<T, S> HttpRequestExtractor<T, S> for Json<T>
where T: DeserializeOwned + 'static, S: 'static
{ {
type Result = Box<Future<Item=Self, Error=Error>>; type Result = Box<Future<Item=Self, Error=Error>>;
#[inline] #[inline]
fn extract<S: 'static>(req: &HttpRequest<S>) -> Self::Result { fn extract(req: &HttpRequest<S>) -> Self::Result {
Box::new( Box::new(
JsonBody::new(req.clone()) JsonBody::new(req.clone())
.from_err() .from_err()
@ -251,7 +252,7 @@ mod tests {
#[test] #[test]
fn test_with_json() { fn test_with_json() {
let mut handler = with(|_: _, data: Json<MyObject>| data); let mut handler = with(|data: Json<MyObject>| data);
let req = HttpRequest::default(); let req = HttpRequest::default();
let mut json = handler.handle(req).into_future(); let mut json = handler.handle(req).into_future();

View File

@ -144,7 +144,7 @@ impl<S: 'static> Resource<S> {
/// ``` /// ```
pub fn with<T, D, H>(&mut self, handler: H) pub fn with<T, D, H>(&mut self, handler: H)
where H: WithHandler<T, D, S>, where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T> + 'static, D: HttpRequestExtractor<T, S> + 'static,
T: DeserializeOwned + 'static, T: DeserializeOwned + 'static,
{ {
self.routes.push(Route::default()); self.routes.push(Route::default());

View File

@ -127,7 +127,7 @@ impl<S: 'static> Route<S> {
/// } /// }
/// ///
/// /// extract path info using serde /// /// extract path info using serde
/// fn index(req: HttpRequest, info: Path<Info>) -> Result<String> { /// fn index(info: Path<Info>) -> Result<String> {
/// Ok(format!("Welcome {}!", info.username)) /// Ok(format!("Welcome {}!", info.username))
/// } /// }
/// ///
@ -139,7 +139,7 @@ impl<S: 'static> Route<S> {
/// ``` /// ```
pub fn with<T, D, H>(&mut self, handler: H) pub fn with<T, D, H>(&mut self, handler: H)
where H: WithHandler<T, D, S>, where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T> + 'static, D: HttpRequestExtractor<T, S> + 'static,
T: DeserializeOwned + 'static, T: DeserializeOwned + 'static,
{ {
self.h(with(handler)) self.h(with(handler))

View File

@ -14,32 +14,33 @@ use extractor::HttpRequestExtractor;
/// Trait defines object that could be registered as route handler /// Trait defines object that could be registered as route handler
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait WithHandler<T, D, S>: 'static pub trait WithHandler<T, D, S>: 'static
where D: HttpRequestExtractor<T>, T: DeserializeOwned where D: HttpRequestExtractor<T, S>, T: DeserializeOwned, S: 'static
{ {
/// The type of value that handler will return. /// The type of value that handler will return.
type Result: Responder; type Result: Responder;
/// Handle request /// Handle request
fn handle(&mut self, req: HttpRequest<S>, data: D) -> Self::Result; fn handle(&mut self, data: D) -> Self::Result;
} }
/// WithHandler<D, T, S> for Fn() /// WithHandler<D, T, S> for Fn()
impl<T, D, S, F, R> WithHandler<T, D, S> for F impl<T, D, S, F, R> WithHandler<T, D, S> for F
where F: Fn(HttpRequest<S>, D) -> R + 'static, where F: Fn(D) -> R + 'static,
R: Responder + 'static, R: Responder + 'static,
D: HttpRequestExtractor<T>, D: HttpRequestExtractor<T, S>,
T: DeserializeOwned, T: DeserializeOwned,
S: 'static,
{ {
type Result = R; type Result = R;
fn handle(&mut self, req: HttpRequest<S>, item: D) -> R { fn handle(&mut self, item: D) -> R {
(self)(req, item) (self)(item)
} }
} }
pub(crate) fn with<T, D, S, H>(h: H) -> With<T, D, S, H> pub(crate) fn with<T, D, S, H>(h: H) -> With<T, D, S, H>
where H: WithHandler<T, D, S>, where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>, D: HttpRequestExtractor<T, S>,
T: DeserializeOwned, T: DeserializeOwned,
{ {
With{hnd: Rc::new(UnsafeCell::new(h)), With{hnd: Rc::new(UnsafeCell::new(h)),
@ -48,8 +49,9 @@ pub(crate) fn with<T, D, S, H>(h: H) -> With<T, D, S, H>
pub struct With<T, D, S, H> pub struct With<T, D, S, H>
where H: WithHandler<T, D, S>, where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>, D: HttpRequestExtractor<T, S>,
T: DeserializeOwned, T: DeserializeOwned,
S: 'static,
{ {
hnd: Rc<UnsafeCell<H>>, hnd: Rc<UnsafeCell<H>>,
_t: PhantomData<T>, _t: PhantomData<T>,
@ -59,9 +61,9 @@ pub struct With<T, D, S, H>
impl<T, D, S, H> Handler<S> for With<T, D, S, H> impl<T, D, S, H> Handler<S> for With<T, D, S, H>
where H: WithHandler<T, D, S>, where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>, D: HttpRequestExtractor<T, S>,
T: DeserializeOwned, T: DeserializeOwned,
T: 'static, D: 'static, S: 'static T: 'static, D: 'static, S: 'static, H: 'static
{ {
type Result = Reply; type Result = Reply;
@ -82,7 +84,7 @@ impl<T, D, S, H> Handler<S> for With<T, D, S, H>
struct WithHandlerFut<T, D, S, H> struct WithHandlerFut<T, D, S, H>
where H: WithHandler<T, D, S>, where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>, D: HttpRequestExtractor<T, S>,
T: DeserializeOwned, T: DeserializeOwned,
T: 'static, D: 'static, S: 'static T: 'static, D: 'static, S: 'static
{ {
@ -96,7 +98,7 @@ struct WithHandlerFut<T, D, S, H>
impl<T, D, S, H> Future for WithHandlerFut<T, D, S, H> impl<T, D, S, H> Future for WithHandlerFut<T, D, S, H>
where H: WithHandler<T, D, S>, where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>, D: HttpRequestExtractor<T, S>,
T: DeserializeOwned, T: DeserializeOwned,
T: 'static, D: 'static, S: 'static T: 'static, D: 'static, S: 'static
{ {
@ -114,7 +116,7 @@ impl<T, D, S, H> Future for WithHandlerFut<T, D, S, H>
}; };
let hnd: &mut H = unsafe{&mut *self.hnd.get()}; let hnd: &mut H = unsafe{&mut *self.hnd.get()};
let item = match hnd.handle(self.req.clone(), item) let item = match hnd.handle(item)
.respond_to(self.req.without_state()) .respond_to(self.req.without_state())
{ {
Ok(item) => item.into(), Ok(item) => item.into(),