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.
fn extract_item(_: HttpRequest, item: Json<MyObj>) -> Result<HttpResponse> {
fn extract_item(item: Json<MyObj>) -> Result<HttpResponse> {
println!("model: {:?}", &item);
httpcodes::HTTPOk.build().json(item.0) // <- send response
}

View File

@ -8,11 +8,11 @@ use error::Error;
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>;
fn extract<S: 'static>(req: &HttpRequest<S>) -> Self::Result;
fn extract(req: &HttpRequest<S>) -> Self::Result;
}
/// 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
/// fn index(req: HttpRequest, info: Path<Info>) -> Result<String> {
/// fn index(info: Path<Info>) -> Result<String> {
/// 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
/// }
/// ```
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;
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 {
&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>;
#[inline]
fn extract<S: 'static>(req: &HttpRequest<S>) -> Self::Result {
result(de::Deserialize::deserialize(PathExtractor{req})
fn extract(req: &HttpRequest<S>) -> Self::Result {
let req = req.clone();
result(de::Deserialize::deserialize(PathExtractor{req: &req})
.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
/// // 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))
/// }
///
@ -100,31 +126,56 @@ impl<T> HttpRequestExtractor<T> for Path<T> where T: DeserializeOwned
/// |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;
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 {
&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>;
#[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())
.map_err(|e| e.into())
.map(Query))
.map(|item| Query{ item, req}))
}
}

View File

@ -136,7 +136,7 @@ pub trait HttpMessage {
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
/// contains decoded parameters.
///
@ -151,15 +151,15 @@ pub trait HttpMessage {
/// ```rust
/// # extern crate actix_web;
/// # extern crate futures;
/// # use futures::Future;
/// 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
/// .from_err()
/// .and_then(|params| { // <- url encoded parameters
/// println!("==== BODY ==== {:?}", params);
/// ok(httpcodes::HttpOk.into())
/// Ok(httpcodes::HttpOk.into())
/// })
/// .responder()
/// }

View File

@ -430,8 +430,8 @@ impl<S> HttpRequest<S> {
where S: 'static,
T: de::DeserializeOwned,
{
match Path::<T>::extract(self).poll()? {
Async::Ready(val) => Ok(val.0),
match Path::<T, _>::extract(self).poll()? {
Async::Ready(val) => Ok(val.into().0),
_ => unreachable!()
}
}
@ -482,8 +482,8 @@ impl<S> HttpRequest<S> {
where S: 'static,
T: de::DeserializeOwned,
{
match Query::<T>::extract(self).poll()? {
Async::Ready(val) => Ok(val.0),
match Query::<T, _>::extract(self).poll()? {
Async::Ready(val) => Ok(val.into().0),
_ => 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>>;
#[inline]
fn extract<S: 'static>(req: &HttpRequest<S>) -> Self::Result {
fn extract(req: &HttpRequest<S>) -> Self::Result {
Box::new(
JsonBody::new(req.clone())
.from_err()
@ -251,7 +252,7 @@ mod tests {
#[test]
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 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)
where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T> + 'static,
D: HttpRequestExtractor<T, S> + 'static,
T: DeserializeOwned + 'static,
{
self.routes.push(Route::default());

View File

@ -127,7 +127,7 @@ impl<S: 'static> Route<S> {
/// }
///
/// /// 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))
/// }
///
@ -139,7 +139,7 @@ impl<S: 'static> Route<S> {
/// ```
pub fn with<T, D, H>(&mut self, handler: H)
where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T> + 'static,
D: HttpRequestExtractor<T, S> + 'static,
T: DeserializeOwned + 'static,
{
self.h(with(handler))

View File

@ -14,32 +14,33 @@ use extractor::HttpRequestExtractor;
/// Trait defines object that could be registered as route handler
#[allow(unused_variables)]
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.
type Result: Responder;
/// 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()
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,
D: HttpRequestExtractor<T>,
D: HttpRequestExtractor<T, S>,
T: DeserializeOwned,
S: 'static,
{
type Result = R;
fn handle(&mut self, req: HttpRequest<S>, item: D) -> R {
(self)(req, item)
fn handle(&mut self, item: D) -> R {
(self)(item)
}
}
pub(crate) fn with<T, D, S, H>(h: H) -> With<T, D, S, H>
where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>,
D: HttpRequestExtractor<T, S>,
T: DeserializeOwned,
{
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>
where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>,
D: HttpRequestExtractor<T, S>,
T: DeserializeOwned,
S: 'static,
{
hnd: Rc<UnsafeCell<H>>,
_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>
where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>,
D: HttpRequestExtractor<T, S>,
T: DeserializeOwned,
T: 'static, D: 'static, S: 'static
T: 'static, D: 'static, S: 'static, H: 'static
{
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>
where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>,
D: HttpRequestExtractor<T, S>,
T: DeserializeOwned,
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>
where H: WithHandler<T, D, S>,
D: HttpRequestExtractor<T>,
D: HttpRequestExtractor<T, S>,
T: DeserializeOwned,
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 item = match hnd.handle(self.req.clone(), item)
let item = match hnd.handle(item)
.respond_to(self.req.without_state())
{
Ok(item) => item.into(),