mirror of
https://github.com/fafhrd91/actix-web
synced 2025-01-18 05:41:50 +01:00
add support for multiple extractors
This commit is contained in:
parent
80f6b93714
commit
65700281e8
@ -8,7 +8,7 @@ use error::Error;
|
|||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
|
|
||||||
|
|
||||||
pub trait HttpRequestExtractor<T, S>: Sized where T: DeserializeOwned, S: 'static
|
pub trait HttpRequestExtractor<S>: Sized where S: 'static
|
||||||
{
|
{
|
||||||
type Result: Future<Item=Self, Error=Error>;
|
type Result: Future<Item=Self, Error=Error>;
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ impl<T, S> Path<T, S> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S> HttpRequestExtractor<T, S> for Path<T, S>
|
impl<T, S> HttpRequestExtractor<S> for Path<T, S>
|
||||||
where T: DeserializeOwned, S: 'static
|
where T: DeserializeOwned, S: 'static
|
||||||
{
|
{
|
||||||
type Result = FutureResult<Self, Error>;
|
type Result = FutureResult<Self, Error>;
|
||||||
@ -175,7 +175,7 @@ impl<T, S> Query<T, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S> HttpRequestExtractor<T, S> for Query<T, S>
|
impl<T, S> HttpRequestExtractor<S> for Query<T, S>
|
||||||
where T: de::DeserializeOwned, S: 'static
|
where T: de::DeserializeOwned, S: 'static
|
||||||
{
|
{
|
||||||
type Result = FutureResult<Self, Error>;
|
type Result = FutureResult<Self, Error>;
|
||||||
|
@ -63,7 +63,7 @@ impl<T: Serialize> Responder for Json<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S> HttpRequestExtractor<T, S> for Json<T>
|
impl<T, S> HttpRequestExtractor<S> for Json<T>
|
||||||
where T: DeserializeOwned + 'static, S: 'static
|
where T: DeserializeOwned + 'static, S: 'static
|
||||||
{
|
{
|
||||||
type Result = Box<Future<Item=Self, Error=Error>>;
|
type Result = Box<Future<Item=Self, Error=Error>>;
|
||||||
|
@ -186,7 +186,7 @@ pub mod dev {
|
|||||||
pub use context::Drain;
|
pub use context::Drain;
|
||||||
pub use info::ConnectionInfo;
|
pub use info::ConnectionInfo;
|
||||||
pub use handler::Handler;
|
pub use handler::Handler;
|
||||||
pub use with::{With, WithHandler};
|
pub use with::WithHandler;
|
||||||
pub use json::JsonBody;
|
pub use json::JsonBody;
|
||||||
pub use router::{Router, Pattern};
|
pub use router::{Router, Pattern};
|
||||||
pub use param::{FromParam, Params};
|
pub use param::{FromParam, Params};
|
||||||
|
@ -3,7 +3,6 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use http::{Method, StatusCode};
|
use http::{Method, StatusCode};
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
|
|
||||||
use pred;
|
use pred;
|
||||||
use body::Body;
|
use body::Body;
|
||||||
@ -142,10 +141,9 @@ impl<S: 'static> Resource<S> {
|
|||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// Resource::resource("/", |r| r.route().with(index)
|
/// Resource::resource("/", |r| r.route().with(index)
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with<T, D, H>(&mut self, handler: H)
|
pub fn with<T, H>(&mut self, handler: H)
|
||||||
where H: WithHandler<T, D, S>,
|
where H: WithHandler<T, S>,
|
||||||
D: HttpRequestExtractor<T, S> + 'static,
|
T: HttpRequestExtractor<S> + 'static,
|
||||||
T: DeserializeOwned + 'static,
|
|
||||||
{
|
{
|
||||||
self.routes.push(Route::default());
|
self.routes.push(Route::default());
|
||||||
self.routes.last_mut().unwrap().with(handler)
|
self.routes.last_mut().unwrap().with(handler)
|
||||||
|
60
src/route.rs
60
src/route.rs
@ -1,7 +1,6 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use futures::{Async, Future, Poll};
|
use futures::{Async, Future, Poll};
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
@ -12,7 +11,7 @@ use middleware::{Middleware, Response as MiddlewareResponse, Started as Middlewa
|
|||||||
use httpcodes::HttpNotFound;
|
use httpcodes::HttpNotFound;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
use with::{with, WithHandler};
|
use with::{with, with2, with3, WithHandler};
|
||||||
use extractor::HttpRequestExtractor;
|
use extractor::HttpRequestExtractor;
|
||||||
|
|
||||||
/// Resource route definition
|
/// Resource route definition
|
||||||
@ -137,13 +136,62 @@ impl<S: 'static> Route<S> {
|
|||||||
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
|
/// |r| r.method(Method::GET).with(index)); // <- use `with` extractor
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with<T, D, H>(&mut self, handler: H)
|
pub fn with<T, H>(&mut self, handler: H)
|
||||||
where H: WithHandler<T, D, S>,
|
where H: WithHandler<T, S>,
|
||||||
D: HttpRequestExtractor<T, S> + 'static,
|
T: HttpRequestExtractor<S> + 'static,
|
||||||
T: DeserializeOwned + 'static,
|
|
||||||
{
|
{
|
||||||
self.h(with(handler))
|
self.h(with(handler))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set handler function, function has to accept two request extractors.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate bytes;
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// # extern crate futures;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// use actix_web::*;
|
||||||
|
/// use actix_web::Path;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct PParam {
|
||||||
|
/// username: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct QParam {
|
||||||
|
/// count: u32,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// extract path info using serde
|
||||||
|
/// fn index(p: Path<PParam>, q: Query<QParam>) -> Result<String> {
|
||||||
|
/// Ok(format!("Welcome {}!", p.username))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = Application::new().resource(
|
||||||
|
/// "/{username}/index.html", // <- define path parameters
|
||||||
|
/// |r| r.method(Method::GET).with2(index)); // <- use `with` extractor
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn with2<T1, T2, F, R>(&mut self, handler: F)
|
||||||
|
where F: Fn(T1, T2) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S> + 'static,
|
||||||
|
T2: HttpRequestExtractor<S> + 'static,
|
||||||
|
{
|
||||||
|
self.h(with2(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with3<T1, T2, T3, F, R>(&mut self, handler: F)
|
||||||
|
where F: Fn(T1, T2, T3) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S> + 'static,
|
||||||
|
T2: HttpRequestExtractor<S> + 'static,
|
||||||
|
T3: HttpRequestExtractor<S> + 'static,
|
||||||
|
{
|
||||||
|
self.h(with3(handler))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `RouteHandler` wrapper. This struct is required because it needs to be shared
|
/// `RouteHandler` wrapper. This struct is required because it needs to be shared
|
||||||
|
10
src/test.rs
10
src/test.rs
@ -27,6 +27,7 @@ use application::{Application, HttpApplication};
|
|||||||
use param::Params;
|
use param::Params;
|
||||||
use router::Router;
|
use router::Router;
|
||||||
use payload::Payload;
|
use payload::Payload;
|
||||||
|
use resource::Resource;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
use server::{HttpServer, IntoHttpHandler, ServerSettings};
|
use server::{HttpServer, IntoHttpHandler, ServerSettings};
|
||||||
@ -395,6 +396,15 @@ impl<S: 'static> TestApp<S> {
|
|||||||
self.app = Some(self.app.take().unwrap().middleware(mw));
|
self.app = Some(self.app.take().unwrap().middleware(mw));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register resource. This method is similar
|
||||||
|
/// to `Application::resource()` method.
|
||||||
|
pub fn resource<F>(&mut self, path: &str, f: F) -> &mut TestApp<S>
|
||||||
|
where F: FnOnce(&mut Resource<S>) + 'static
|
||||||
|
{
|
||||||
|
self.app = Some(self.app.take().unwrap().resource(path, f));
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: 'static> IntoHttpHandler for TestApp<S> {
|
impl<S: 'static> IntoHttpHandler for TestApp<S> {
|
||||||
|
315
src/with.rs
315
src/with.rs
@ -1,7 +1,6 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use futures::{Async, Future, Poll};
|
use futures::{Async, Future, Poll};
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
@ -13,62 +12,57 @@ 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, S>: 'static
|
||||||
where D: HttpRequestExtractor<T, S>, T: DeserializeOwned, S: 'static
|
where T: HttpRequestExtractor<S>, 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, data: D) -> Self::Result;
|
fn handle(&mut self, data: T) -> 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, S, F, R> WithHandler<T, S> for F
|
||||||
where F: Fn(D) -> R + 'static,
|
where F: Fn(T) -> R + 'static,
|
||||||
R: Responder + 'static,
|
R: Responder + 'static,
|
||||||
D: HttpRequestExtractor<T, S>,
|
T: HttpRequestExtractor<S>,
|
||||||
T: DeserializeOwned,
|
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
type Result = R;
|
type Result = R;
|
||||||
|
|
||||||
fn handle(&mut self, item: D) -> R {
|
fn handle(&mut self, item: T) -> R {
|
||||||
(self)(item)
|
(self)(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn with<T, D, S, H>(h: H) -> With<T, D, S, H>
|
pub(crate)
|
||||||
where H: WithHandler<T, D, S>,
|
fn with<T, S, H>(h: H) -> With<T, S, H>
|
||||||
D: HttpRequestExtractor<T, S>,
|
where H: WithHandler<T, S>,
|
||||||
T: DeserializeOwned,
|
T: HttpRequestExtractor<S>,
|
||||||
{
|
{
|
||||||
With{hnd: Rc::new(UnsafeCell::new(h)),
|
With{hnd: Rc::new(UnsafeCell::new(h)), _t: PhantomData, _s: PhantomData}
|
||||||
_t: PhantomData, _d: PhantomData, _s: PhantomData}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct With<T, D, S, H>
|
pub struct With<T, S, H>
|
||||||
where H: WithHandler<T, D, S>,
|
where H: WithHandler<T, S>,
|
||||||
D: HttpRequestExtractor<T, S>,
|
T: HttpRequestExtractor<S>,
|
||||||
T: DeserializeOwned,
|
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
hnd: Rc<UnsafeCell<H>>,
|
hnd: Rc<UnsafeCell<H>>,
|
||||||
_t: PhantomData<T>,
|
_t: PhantomData<T>,
|
||||||
_d: PhantomData<D>,
|
|
||||||
_s: PhantomData<S>,
|
_s: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, D, S, H> Handler<S> for With<T, D, S, H>
|
impl<T, S, H> Handler<S> for With<T, S, H>
|
||||||
where H: WithHandler<T, D, S>,
|
where H: WithHandler<T, S>,
|
||||||
D: HttpRequestExtractor<T, S>,
|
T: HttpRequestExtractor<S> + 'static,
|
||||||
T: DeserializeOwned,
|
S: 'static, H: 'static
|
||||||
T: 'static, D: 'static, S: 'static, H: 'static
|
|
||||||
{
|
{
|
||||||
type Result = Reply;
|
type Result = Reply;
|
||||||
|
|
||||||
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
|
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
|
||||||
let fut = Box::new(D::extract(&req));
|
let fut = Box::new(T::extract(&req));
|
||||||
|
|
||||||
Reply::async(
|
Reply::async(
|
||||||
WithHandlerFut{
|
WithHandlerFut{
|
||||||
@ -76,31 +70,25 @@ impl<T, D, S, H> Handler<S> for With<T, D, S, H>
|
|||||||
hnd: Rc::clone(&self.hnd),
|
hnd: Rc::clone(&self.hnd),
|
||||||
fut1: Some(fut),
|
fut1: Some(fut),
|
||||||
fut2: None,
|
fut2: None,
|
||||||
_t: PhantomData,
|
|
||||||
_d: PhantomData,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WithHandlerFut<T, D, S, H>
|
struct WithHandlerFut<T, S, H>
|
||||||
where H: WithHandler<T, D, S>,
|
where H: WithHandler<T, S>,
|
||||||
D: HttpRequestExtractor<T, S>,
|
T: HttpRequestExtractor<S>,
|
||||||
T: DeserializeOwned,
|
T: 'static, S: 'static
|
||||||
T: 'static, D: 'static, S: 'static
|
|
||||||
{
|
{
|
||||||
hnd: Rc<UnsafeCell<H>>,
|
hnd: Rc<UnsafeCell<H>>,
|
||||||
req: HttpRequest<S>,
|
req: HttpRequest<S>,
|
||||||
fut1: Option<Box<Future<Item=D, Error=Error>>>,
|
fut1: Option<Box<Future<Item=T, Error=Error>>>,
|
||||||
fut2: Option<Box<Future<Item=HttpResponse, Error=Error>>>,
|
fut2: Option<Box<Future<Item=HttpResponse, Error=Error>>>,
|
||||||
_t: PhantomData<T>,
|
|
||||||
_d: PhantomData<D>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, D, S, H> Future for WithHandlerFut<T, D, S, H>
|
impl<T, S, H> Future for WithHandlerFut<T, S, H>
|
||||||
where H: WithHandler<T, D, S>,
|
where H: WithHandler<T, S>,
|
||||||
D: HttpRequestExtractor<T, S>,
|
T: HttpRequestExtractor<S> + 'static,
|
||||||
T: DeserializeOwned,
|
S: 'static
|
||||||
T: 'static, D: 'static, S: 'static
|
|
||||||
{
|
{
|
||||||
type Item = HttpResponse;
|
type Item = HttpResponse;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
@ -131,3 +119,248 @@ impl<T, D, S, H> Future for WithHandlerFut<T, D, S, H>
|
|||||||
self.poll()
|
self.poll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate)
|
||||||
|
fn with2<T1, T2, S, F, R>(h: F) -> With2<T1, T2, S, F, R>
|
||||||
|
where F: Fn(T1, T2) -> R,
|
||||||
|
R: Responder,
|
||||||
|
T1: HttpRequestExtractor<S>,
|
||||||
|
T2: HttpRequestExtractor<S>,
|
||||||
|
{
|
||||||
|
With2{hnd: Rc::new(UnsafeCell::new(h)),
|
||||||
|
_t1: PhantomData, _t2: PhantomData, _s: PhantomData}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct With2<T1, T2, S, F, R>
|
||||||
|
where F: Fn(T1, T2) -> R,
|
||||||
|
R: Responder,
|
||||||
|
T1: HttpRequestExtractor<S>,
|
||||||
|
T2: HttpRequestExtractor<S>,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
hnd: Rc<UnsafeCell<F>>,
|
||||||
|
_t1: PhantomData<T1>,
|
||||||
|
_t2: PhantomData<T2>,
|
||||||
|
_s: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T1, T2, S, F, R> Handler<S> for With2<T1, T2, S, F, R>
|
||||||
|
where F: Fn(T1, T2) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S> + 'static,
|
||||||
|
T2: HttpRequestExtractor<S> + 'static,
|
||||||
|
S: 'static
|
||||||
|
{
|
||||||
|
type Result = Reply;
|
||||||
|
|
||||||
|
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
|
||||||
|
let fut = Box::new(T1::extract(&req));
|
||||||
|
|
||||||
|
Reply::async(
|
||||||
|
WithHandlerFut2{
|
||||||
|
req,
|
||||||
|
hnd: Rc::clone(&self.hnd),
|
||||||
|
item: None,
|
||||||
|
fut1: Some(fut),
|
||||||
|
fut2: None,
|
||||||
|
fut3: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WithHandlerFut2<T1, T2, S, F, R>
|
||||||
|
where F: Fn(T1, T2) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S> + 'static,
|
||||||
|
T2: HttpRequestExtractor<S> + 'static,
|
||||||
|
S: 'static
|
||||||
|
{
|
||||||
|
hnd: Rc<UnsafeCell<F>>,
|
||||||
|
req: HttpRequest<S>,
|
||||||
|
item: Option<T1>,
|
||||||
|
fut1: Option<Box<Future<Item=T1, Error=Error>>>,
|
||||||
|
fut2: Option<Box<Future<Item=T2, Error=Error>>>,
|
||||||
|
fut3: Option<Box<Future<Item=HttpResponse, Error=Error>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T1, T2, S, F, R> Future for WithHandlerFut2<T1, T2, S, F, R>
|
||||||
|
where F: Fn(T1, T2) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S> + 'static,
|
||||||
|
T2: HttpRequestExtractor<S> + 'static,
|
||||||
|
S: 'static
|
||||||
|
{
|
||||||
|
type Item = HttpResponse;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
|
if let Some(ref mut fut) = self.fut3 {
|
||||||
|
return fut.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.fut1.is_some() {
|
||||||
|
match self.fut1.as_mut().unwrap().poll()? {
|
||||||
|
Async::Ready(item) => {
|
||||||
|
self.item = Some(item);
|
||||||
|
self.fut1.take();
|
||||||
|
self.fut2 = Some(Box::new(T2::extract(&self.req)));
|
||||||
|
},
|
||||||
|
Async::NotReady => return Ok(Async::NotReady),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = match self.fut2.as_mut().unwrap().poll()? {
|
||||||
|
Async::Ready(item) => item,
|
||||||
|
Async::NotReady => return Ok(Async::NotReady),
|
||||||
|
};
|
||||||
|
|
||||||
|
let hnd: &mut F = unsafe{&mut *self.hnd.get()};
|
||||||
|
let item = match (*hnd)(self.item.take().unwrap(), item)
|
||||||
|
.respond_to(self.req.without_state())
|
||||||
|
{
|
||||||
|
Ok(item) => item.into(),
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match item.into() {
|
||||||
|
ReplyItem::Message(resp) => return Ok(Async::Ready(resp)),
|
||||||
|
ReplyItem::Future(fut) => self.fut3 = Some(fut),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate)
|
||||||
|
fn with3<T1, T2, T3, S, F, R>(h: F) -> With3<T1, T2, T3, S, F, R>
|
||||||
|
where F: Fn(T1, T2, T3) -> R + 'static,
|
||||||
|
R: Responder,
|
||||||
|
T1: HttpRequestExtractor<S>,
|
||||||
|
T2: HttpRequestExtractor<S>,
|
||||||
|
T3: HttpRequestExtractor<S>,
|
||||||
|
{
|
||||||
|
With3{hnd: Rc::new(UnsafeCell::new(h)),
|
||||||
|
_s: PhantomData, _t1: PhantomData, _t2: PhantomData, _t3: PhantomData}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct With3<T1, T2, T3, S, F, R>
|
||||||
|
where F: Fn(T1, T2, T3) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S>,
|
||||||
|
T2: HttpRequestExtractor<S>,
|
||||||
|
T3: HttpRequestExtractor<S>,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
hnd: Rc<UnsafeCell<F>>,
|
||||||
|
_t1: PhantomData<T1>,
|
||||||
|
_t2: PhantomData<T2>,
|
||||||
|
_t3: PhantomData<T3>,
|
||||||
|
_s: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T1, T2, T3, S, F, R> Handler<S> for With3<T1, T2, T3, S, F, R>
|
||||||
|
where F: Fn(T1, T2, T3) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S>,
|
||||||
|
T2: HttpRequestExtractor<S>,
|
||||||
|
T3: HttpRequestExtractor<S>,
|
||||||
|
T1: 'static, T2: 'static, T3: 'static, S: 'static
|
||||||
|
{
|
||||||
|
type Result = Reply;
|
||||||
|
|
||||||
|
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
|
||||||
|
let fut = Box::new(T1::extract(&req));
|
||||||
|
|
||||||
|
Reply::async(
|
||||||
|
WithHandlerFut3{
|
||||||
|
req,
|
||||||
|
hnd: Rc::clone(&self.hnd),
|
||||||
|
item1: None,
|
||||||
|
item2: None,
|
||||||
|
fut1: Some(fut),
|
||||||
|
fut2: None,
|
||||||
|
fut3: None,
|
||||||
|
fut4: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WithHandlerFut3<T1, T2, T3, S, F, R>
|
||||||
|
where F: Fn(T1, T2, T3) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S> + 'static,
|
||||||
|
T2: HttpRequestExtractor<S> + 'static,
|
||||||
|
T3: HttpRequestExtractor<S> + 'static,
|
||||||
|
S: 'static
|
||||||
|
{
|
||||||
|
hnd: Rc<UnsafeCell<F>>,
|
||||||
|
req: HttpRequest<S>,
|
||||||
|
item1: Option<T1>,
|
||||||
|
item2: Option<T2>,
|
||||||
|
fut1: Option<Box<Future<Item=T1, Error=Error>>>,
|
||||||
|
fut2: Option<Box<Future<Item=T2, Error=Error>>>,
|
||||||
|
fut3: Option<Box<Future<Item=T3, Error=Error>>>,
|
||||||
|
fut4: Option<Box<Future<Item=HttpResponse, Error=Error>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T1, T2, T3, S, F, R> Future for WithHandlerFut3<T1, T2, T3, S, F, R>
|
||||||
|
where F: Fn(T1, T2, T3) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T1: HttpRequestExtractor<S> + 'static,
|
||||||
|
T2: HttpRequestExtractor<S> + 'static,
|
||||||
|
T3: HttpRequestExtractor<S> + 'static,
|
||||||
|
S: 'static
|
||||||
|
{
|
||||||
|
type Item = HttpResponse;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
|
if let Some(ref mut fut) = self.fut4 {
|
||||||
|
return fut.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.fut1.is_some() {
|
||||||
|
match self.fut1.as_mut().unwrap().poll()? {
|
||||||
|
Async::Ready(item) => {
|
||||||
|
self.item1 = Some(item);
|
||||||
|
self.fut1.take();
|
||||||
|
self.fut2 = Some(Box::new(T2::extract(&self.req)));
|
||||||
|
},
|
||||||
|
Async::NotReady => return Ok(Async::NotReady),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.fut2.is_some() {
|
||||||
|
match self.fut2.as_mut().unwrap().poll()? {
|
||||||
|
Async::Ready(item) => {
|
||||||
|
self.item2 = Some(item);
|
||||||
|
self.fut2.take();
|
||||||
|
self.fut3 = Some(Box::new(T3::extract(&self.req)));
|
||||||
|
},
|
||||||
|
Async::NotReady => return Ok(Async::NotReady),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = match self.fut3.as_mut().unwrap().poll()? {
|
||||||
|
Async::Ready(item) => item,
|
||||||
|
Async::NotReady => return Ok(Async::NotReady),
|
||||||
|
};
|
||||||
|
|
||||||
|
let hnd: &mut F = unsafe{&mut *self.hnd.get()};
|
||||||
|
let item = match (*hnd)(self.item1.take().unwrap(),
|
||||||
|
self.item2.take().unwrap(),
|
||||||
|
item)
|
||||||
|
.respond_to(self.req.without_state())
|
||||||
|
{
|
||||||
|
Ok(item) => item.into(),
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match item.into() {
|
||||||
|
ReplyItem::Message(resp) => return Ok(Async::Ready(resp)),
|
||||||
|
ReplyItem::Future(fut) => self.fut4 = Some(fut),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
63
tests/test_handlers.rs
Normal file
63
tests/test_handlers.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
extern crate actix;
|
||||||
|
extern crate actix_web;
|
||||||
|
extern crate tokio_core;
|
||||||
|
extern crate futures;
|
||||||
|
extern crate h2;
|
||||||
|
extern crate http;
|
||||||
|
extern crate bytes;
|
||||||
|
#[macro_use] extern crate serde_derive;
|
||||||
|
|
||||||
|
use actix_web::*;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use http::StatusCode;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct PParam {
|
||||||
|
username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_path_extractor() {
|
||||||
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
app.resource(
|
||||||
|
"/{username}/index.html", |r| r.with(
|
||||||
|
|p: Path<PParam>| format!("Welcome {}!", p.username)));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.get().uri(srv.url("/test/index.html"))
|
||||||
|
.finish().unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.execute(response.body()).unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(b"Welcome test!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_extractor() {
|
||||||
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
app.resource(
|
||||||
|
"/index.html", |r| r.with(
|
||||||
|
|p: Query<PParam>| format!("Welcome {}!", p.username)));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.get().uri(srv.url("/index.html?username=test"))
|
||||||
|
.finish().unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.execute(response.body()).unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(b"Welcome test!"));
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.get().uri(srv.url("/index.html"))
|
||||||
|
.finish().unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user