mirror of
https://github.com/actix/actix-extras.git
synced 2025-06-25 18:09:22 +02:00
Add Router::with_async() method for async handler registration
This commit is contained in:
@ -1,9 +1,11 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::Future;
|
||||
use http::{Method, StatusCode};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use error::Error;
|
||||
use handler::{AsyncResult, FromRequest, Handler, Responder};
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
@ -183,6 +185,25 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
self.routes.last_mut().unwrap().with(handler);
|
||||
}
|
||||
|
||||
/// Register a new route and add async handler.
|
||||
///
|
||||
/// This is shortcut for:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// Application::resource("/", |r| r.route().with_async(index)
|
||||
/// ```
|
||||
pub fn with_async<T, F, R, I, E>(&mut self, handler: F)
|
||||
where
|
||||
F: Fn(T) -> R + 'static,
|
||||
R: Future<Item = I, Error = E> + 'static,
|
||||
I: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
T: FromRequest<S> + 'static,
|
||||
{
|
||||
self.routes.push(Route::default());
|
||||
self.routes.last_mut().unwrap().with_async(handler);
|
||||
}
|
||||
|
||||
/// Register a resource middleware
|
||||
///
|
||||
/// This is similar to `App's` middlewares, but
|
||||
|
72
src/route.rs
72
src/route.rs
@ -13,7 +13,7 @@ use httpresponse::HttpResponse;
|
||||
use middleware::{Finished as MiddlewareFinished, Middleware,
|
||||
Response as MiddlewareResponse, Started as MiddlewareStarted};
|
||||
use pred::Predicate;
|
||||
use with::{ExtractorConfig, With, With2, With3};
|
||||
use with::{ExtractorConfig, With, With2, With3, WithAsync};
|
||||
|
||||
/// Resource route definition
|
||||
///
|
||||
@ -129,6 +129,34 @@ impl<S: 'static> Route<S> {
|
||||
/// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// It is possible to use tuples for specifing multiple extractors for one
|
||||
/// handler function.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate bytes;
|
||||
/// # extern crate actix_web;
|
||||
/// # extern crate futures;
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// # use std::collections::HashMap;
|
||||
/// use actix_web::{http, App, Query, Path, Result, Json};
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
/// username: String,
|
||||
/// }
|
||||
///
|
||||
/// /// extract path info using serde
|
||||
/// fn index(info: (Path<Info>, Query<HashMap<String, String>>, Json<Info>)) -> Result<String> {
|
||||
/// Ok(format!("Welcome {}!", info.0.username))
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().resource(
|
||||
/// "/{username}/index.html", // <- define path parameters
|
||||
/// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
||||
/// }
|
||||
/// ```
|
||||
pub fn with<T, F, R>(&mut self, handler: F) -> ExtractorConfig<S, T>
|
||||
where
|
||||
F: Fn(T) -> R + 'static,
|
||||
@ -140,6 +168,47 @@ impl<S: 'static> Route<S> {
|
||||
cfg
|
||||
}
|
||||
|
||||
/// Set async handler function, use request extractor for parameters.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate bytes;
|
||||
/// # extern crate actix_web;
|
||||
/// # extern crate futures;
|
||||
/// #[macro_use] extern crate serde_derive;
|
||||
/// use actix_web::{App, Path, Error, http};
|
||||
/// use futures::Future;
|
||||
///
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Info {
|
||||
/// username: String,
|
||||
/// }
|
||||
///
|
||||
/// /// extract path info using serde
|
||||
/// fn index(info: Path<Info>) -> Box<Future<Item=&'static str, Error=Error>> {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().resource(
|
||||
/// "/{username}/index.html", // <- define path parameters
|
||||
/// |r| r.method(http::Method::GET)
|
||||
/// .with_async(index)); // <- use `with` extractor
|
||||
/// }
|
||||
/// ```
|
||||
pub fn with_async<T, F, R, I, E>(&mut self, handler: F) -> ExtractorConfig<S, T>
|
||||
where
|
||||
F: Fn(T) -> R + 'static,
|
||||
R: Future<Item = I, Error = E> + 'static,
|
||||
I: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
T: FromRequest<S> + 'static,
|
||||
{
|
||||
let cfg = ExtractorConfig::default();
|
||||
self.h(WithAsync::new(handler, Clone::clone(&cfg)));
|
||||
cfg
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Set handler function, use request extractor for both parameters.
|
||||
///
|
||||
/// ```rust
|
||||
@ -189,6 +258,7 @@ impl<S: 'static> Route<S> {
|
||||
(cfg1, cfg2)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Set handler function, use request extractor for all parameters.
|
||||
pub fn with3<T1, T2, T3, F, R>(
|
||||
&mut self, handler: F,
|
||||
|
139
src/with.rs
139
src/with.rs
@ -167,6 +167,145 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WithAsync<T, S, F, R, I, E>
|
||||
where
|
||||
F: Fn(T) -> R,
|
||||
R: Future<Item = I, Error = E>,
|
||||
I: Responder,
|
||||
E: Into<E>,
|
||||
T: FromRequest<S>,
|
||||
S: 'static,
|
||||
{
|
||||
hnd: Rc<UnsafeCell<F>>,
|
||||
cfg: ExtractorConfig<S, T>,
|
||||
_s: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<T, S, F, R, I, E> WithAsync<T, S, F, R, I, E>
|
||||
where
|
||||
F: Fn(T) -> R,
|
||||
R: Future<Item = I, Error = E>,
|
||||
I: Responder,
|
||||
E: Into<Error>,
|
||||
T: FromRequest<S>,
|
||||
S: 'static,
|
||||
{
|
||||
pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self {
|
||||
WithAsync {
|
||||
cfg,
|
||||
hnd: Rc::new(UnsafeCell::new(f)),
|
||||
_s: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S, F, R, I, E> Handler<S> for WithAsync<T, S, F, R, I, E>
|
||||
where
|
||||
F: Fn(T) -> R + 'static,
|
||||
R: Future<Item = I, Error = E> + 'static,
|
||||
I: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
T: FromRequest<S> + 'static,
|
||||
S: 'static,
|
||||
{
|
||||
type Result = AsyncResult<HttpResponse>;
|
||||
|
||||
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
|
||||
let mut fut = WithAsyncHandlerFut {
|
||||
req,
|
||||
started: false,
|
||||
hnd: Rc::clone(&self.hnd),
|
||||
cfg: self.cfg.clone(),
|
||||
fut1: None,
|
||||
fut2: None,
|
||||
fut3: None,
|
||||
};
|
||||
|
||||
match fut.poll() {
|
||||
Ok(Async::Ready(resp)) => AsyncResult::ok(resp),
|
||||
Ok(Async::NotReady) => AsyncResult::async(Box::new(fut)),
|
||||
Err(e) => AsyncResult::err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WithAsyncHandlerFut<T, S, F, R, I, E>
|
||||
where
|
||||
F: Fn(T) -> R,
|
||||
R: Future<Item = I, Error = E> + 'static,
|
||||
I: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
T: FromRequest<S> + 'static,
|
||||
S: 'static,
|
||||
{
|
||||
started: bool,
|
||||
hnd: Rc<UnsafeCell<F>>,
|
||||
cfg: ExtractorConfig<S, T>,
|
||||
req: HttpRequest<S>,
|
||||
fut1: Option<Box<Future<Item = T, Error = Error>>>,
|
||||
fut2: Option<R>,
|
||||
fut3: Option<Box<Future<Item = HttpResponse, Error = Error>>>,
|
||||
}
|
||||
|
||||
impl<T, S, F, R, I, E> Future for WithAsyncHandlerFut<T, S, F, R, I, E>
|
||||
where
|
||||
F: Fn(T) -> R,
|
||||
R: Future<Item = I, Error = E> + 'static,
|
||||
I: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
T: FromRequest<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.fut2.is_some() {
|
||||
return match self.fut2.as_mut().unwrap().poll() {
|
||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
||||
Ok(Async::Ready(r)) => match r.respond_to(&self.req) {
|
||||
Ok(r) => match r.into().into() {
|
||||
AsyncResultItem::Err(err) => Err(err),
|
||||
AsyncResultItem::Ok(resp) => Ok(Async::Ready(resp)),
|
||||
AsyncResultItem::Future(fut) => {
|
||||
self.fut3 = Some(fut);
|
||||
self.poll()
|
||||
}
|
||||
},
|
||||
Err(e) => Err(e.into()),
|
||||
},
|
||||
Err(e) => Err(e.into()),
|
||||
};
|
||||
}
|
||||
|
||||
let item = if !self.started {
|
||||
self.started = true;
|
||||
let reply = T::from_request(&self.req, self.cfg.as_ref()).into();
|
||||
match reply.into() {
|
||||
AsyncResultItem::Err(err) => return Err(err),
|
||||
AsyncResultItem::Ok(msg) => msg,
|
||||
AsyncResultItem::Future(fut) => {
|
||||
self.fut1 = Some(fut);
|
||||
return self.poll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self.fut1.as_mut().unwrap().poll()? {
|
||||
Async::Ready(item) => item,
|
||||
Async::NotReady => return Ok(Async::NotReady),
|
||||
}
|
||||
};
|
||||
|
||||
let hnd: &mut F = unsafe { &mut *self.hnd.get() };
|
||||
self.fut2 = Some((*hnd)(item));
|
||||
self.poll()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct With2<T1, T2, S, F, R>
|
||||
where
|
||||
F: Fn(T1, T2) -> R,
|
||||
|
Reference in New Issue
Block a user