1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-30 18:44:35 +01:00

drop request's extract_xxx methods

This commit is contained in:
Nikolay Kim 2018-03-29 09:26:01 -07:00
parent 9e61c67128
commit 7d6deab9fb
5 changed files with 172 additions and 199 deletions

View File

@ -2,9 +2,7 @@
## 0.4.11 ## 0.4.11
* Added `Route::with()` handler, uses request extractor * Type-safe path/query parameter handling, using serde #70
* Added `HttpReuqest::extract_xxx()`, type safe path/query information extractor
* Router cannot parse Non-ASCII characters in URL #137 * Router cannot parse Non-ASCII characters in URL #137

View File

@ -305,6 +305,8 @@ impl<'de, S: 'de> Deserializer<'de> for PathExtractor<'de, S>
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures::Async;
use super::*;
use router::{Router, Pattern}; use router::{Router, Pattern};
use resource::Resource; use resource::Resource;
use test::TestRequest; use test::TestRequest;
@ -332,15 +334,27 @@ 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());
let s: MyStruct = req.extract_path().unwrap(); match Path::<MyStruct, _>::extract(&req).poll().unwrap() {
assert_eq!(s.key, "name"); Async::Ready(s) => {
assert_eq!(s.value, "user1"); assert_eq!(s.key, "name");
assert_eq!(s.value, "user1");
},
_ => unreachable!(),
}
let s: (String, String) = req.extract_path().unwrap(); match Path::<(String, String), _>::extract(&req).poll().unwrap() {
assert_eq!(s.0, "name"); Async::Ready(s) => {
assert_eq!(s.1, "user1"); assert_eq!(s.0, "name");
assert_eq!(s.1, "user1");
},
_ => unreachable!(),
}
let s: Id = req.extract_query().unwrap(); match Query::<Id, _>::extract(&req).poll().unwrap() {
assert_eq!(s.id, "test"); Async::Ready(s) => {
assert_eq!(s.id, "test");
},
_ => unreachable!(),
}
} }
} }

View File

@ -155,14 +155,6 @@ impl Reply {
_ => None, _ => None,
} }
} }
#[cfg(test)]
pub(crate) fn into_future(self) -> Box<Future<Item=HttpResponse, Error=Error>> {
match self.0 {
ReplyItem::Future(fut) => fut,
_ => panic!(),
}
}
} }
impl Responder for Reply { impl Responder for Reply {

View File

@ -5,13 +5,12 @@ use std::net::SocketAddr;
use std::borrow::Cow; use std::borrow::Cow;
use bytes::Bytes; use bytes::Bytes;
use cookie::Cookie; use cookie::Cookie;
use futures::{Async, Future, Stream, Poll}; use futures::{Async, Stream, Poll};
use futures_cpupool::CpuPool; use futures_cpupool::CpuPool;
use failure; use failure;
use url::{Url, form_urlencoded}; use url::{Url, form_urlencoded};
use http::{header, Uri, Method, Version, HeaderMap, Extensions, StatusCode}; use http::{header, Uri, Method, Version, HeaderMap, Extensions, StatusCode};
use tokio_io::AsyncRead; use tokio_io::AsyncRead;
use serde::de;
use percent_encoding::percent_decode; use percent_encoding::percent_decode;
use body::Body; use body::Body;
@ -22,8 +21,7 @@ use payload::Payload;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use httpresponse::{HttpResponse, HttpResponseBuilder}; use httpresponse::{HttpResponse, HttpResponseBuilder};
use helpers::SharedHttpInnerMessage; use helpers::SharedHttpInnerMessage;
use extractor::{Path, Query, HttpRequestExtractor}; use error::{UrlGenerationError, CookieParseError, PayloadError};
use error::{Error, UrlGenerationError, CookieParseError, PayloadError};
pub struct HttpInnerMessage { pub struct HttpInnerMessage {
@ -406,96 +404,6 @@ impl<S> HttpRequest<S> {
unsafe{ mem::transmute(&mut self.as_mut().params) } unsafe{ mem::transmute(&mut self.as_mut().params) }
} }
/// Extract typed information from request's path.
///
/// By default, in case of error `BAD_REQUEST` response get returned to peer.
/// If you need to return different response use `map_err()` method.
///
/// ## Example
///
/// ```rust
/// # extern crate actix_web;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::*;
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// fn index(mut req: HttpRequest) -> Result<String> {
/// let info: Info = req.extract_path()?; // <- extract path info using serde
/// Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
/// let app = Application::new()
/// .resource("/{username}/index.html", // <- define path parameters
/// |r| r.method(Method::GET).f(index));
/// }
/// ```
pub fn extract_path<T>(&self) -> Result<T, Error>
where S: 'static,
T: de::DeserializeOwned,
{
match Path::<T, _>::extract(self).poll()? {
Async::Ready(val) => Ok(val.into().0),
_ => unreachable!()
}
}
/// Extract typed information from request's query string.
///
/// ## Example
///
/// ```rust
/// # extern crate actix_web;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::{HttpRequest, Result};
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// fn index(mut req: HttpRequest) -> Result<String> {
/// let info: Info = req.extract_query()?; // <- extract query info, i.e: /?id=username
/// Ok(format!("Welcome {}!", info.username))
/// }
/// # fn main() {}
/// ```
///
/// By default, in case of error, `BAD_REQUEST` response get returned to peer.
/// If you need to return different response use `map_err()` method.
///
/// ```rust
/// # extern crate actix_web;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::{HttpRequest, Result, error};
///
/// #[derive(Deserialize)]
/// struct Info {
/// username: String,
/// }
///
/// fn index(mut req: HttpRequest) -> Result<String> {
/// let info: Info = req.extract_query() // <- extract query information
/// .map_err(error::ErrorInternalServerError)?; // <- return 500 in case of error
/// Ok(format!("Welcome {}!", info.username))
/// }
/// # fn main() {}
/// ```
///
pub fn extract_query<T>(&self) -> Result<T, Error>
where S: 'static,
T: de::DeserializeOwned,
{
match Query::<T, _>::extract(self).poll()? {
Async::Ready(val) => Ok(val.into().0),
_ => unreachable!()
}
}
/// Checks if a connection should be kept alive. /// Checks if a connection should be kept alive.
pub fn keep_alive(&self) -> bool { pub fn keep_alive(&self) -> bool {
self.as_ref().keep_alive() self.as_ref().keep_alive()

View File

@ -62,31 +62,17 @@ impl<T, S, H> Handler<S> for With<T, S, H>
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 mut fut = T::extract(&req); let mut fut = WithHandlerFut{
req,
started: false,
hnd: Rc::clone(&self.hnd),
fut1: None,
fut2: None,
};
match fut.poll() { match fut.poll() {
Ok(Async::Ready(item)) => { Ok(Async::Ready(resp)) => Reply::response(resp),
let hnd: &mut H = unsafe{&mut *self.hnd.get()}; Ok(Async::NotReady) => Reply::async(fut),
match hnd.handle(item).respond_to(req.without_state()) {
Ok(item) => match item.into().into() {
ReplyItem::Message(resp) => Reply::response(resp),
ReplyItem::Future(fut) => Reply::async(
WithHandlerFut{
req,
hnd: Rc::clone(&self.hnd),
fut1: None,
fut2: Some(fut),
})
},
Err(e) => Reply::response(e.into()),
}
}
Ok(Async::NotReady) => Reply::async(
WithHandlerFut{
req,
hnd: Rc::clone(&self.hnd),
fut1: Some(Box::new(fut)),
fut2: None,
}),
Err(e) => Reply::response(e), Err(e) => Reply::response(e),
} }
} }
@ -97,6 +83,7 @@ struct WithHandlerFut<T, S, H>
T: HttpRequestExtractor<S>, T: HttpRequestExtractor<S>,
T: 'static, S: 'static T: 'static, S: 'static
{ {
started: bool,
hnd: Rc<UnsafeCell<H>>, hnd: Rc<UnsafeCell<H>>,
req: HttpRequest<S>, req: HttpRequest<S>,
fut1: Option<Box<Future<Item=T, Error=Error>>>, fut1: Option<Box<Future<Item=T, Error=Error>>>,
@ -116,15 +103,26 @@ impl<T, S, H> Future for WithHandlerFut<T, S, H>
return fut.poll() return fut.poll()
} }
let item = match self.fut1.as_mut().unwrap().poll()? { let item = if !self.started {
Async::Ready(item) => item, self.started = true;
Async::NotReady => return Ok(Async::NotReady), let mut fut = T::extract(&self.req);
match fut.poll() {
Ok(Async::Ready(item)) => item,
Ok(Async::NotReady) => {
self.fut1 = Some(Box::new(fut));
return Ok(Async::NotReady)
},
Err(e) => return Err(e),
}
} else {
match self.fut1.as_mut().unwrap().poll()? {
Async::Ready(item) => item,
Async::NotReady => return Ok(Async::NotReady),
}
}; };
let hnd: &mut H = unsafe{&mut *self.hnd.get()}; let hnd: &mut H = unsafe{&mut *self.hnd.get()};
let item = match hnd.handle(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(),
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
}; };
@ -172,50 +170,18 @@ impl<T1, T2, S, F, R> Handler<S> for With2<T1, T2, S, F, R>
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 mut fut = T1::extract(&req); let mut fut = WithHandlerFut2{
req,
started: false,
hnd: Rc::clone(&self.hnd),
item: None,
fut1: None,
fut2: None,
fut3: None,
};
match fut.poll() { match fut.poll() {
Ok(Async::Ready(item1)) => { Ok(Async::Ready(resp)) => Reply::response(resp),
let mut fut = T2::extract(&req); Ok(Async::NotReady) => Reply::async(fut),
match fut.poll() {
Ok(Async::Ready(item2)) => {
let hnd: &mut F = unsafe{&mut *self.hnd.get()};
match (*hnd)(item1, item2).respond_to(req.without_state()) {
Ok(item) => match item.into().into() {
ReplyItem::Message(resp) => Reply::response(resp),
ReplyItem::Future(fut) => Reply::async(
WithHandlerFut2{
req,
item: None,
hnd: Rc::clone(&self.hnd),
fut1: None,
fut2: None,
fut3: Some(fut),
})
},
Err(e) => Reply::response(e.into()),
}
},
Ok(Async::NotReady) => Reply::async(
WithHandlerFut2{
req,
hnd: Rc::clone(&self.hnd),
item: Some(item1),
fut1: None,
fut2: Some(Box::new(fut)),
fut3: None,
}),
Err(e) => Reply::response(e),
}
},
Ok(Async::NotReady) => Reply::async(
WithHandlerFut2{
req,
hnd: Rc::clone(&self.hnd),
item: None,
fut1: Some(Box::new(fut)),
fut2: None,
fut3: None,
}),
Err(e) => Reply::response(e), Err(e) => Reply::response(e),
} }
} }
@ -228,6 +194,7 @@ struct WithHandlerFut2<T1, T2, S, F, R>
T2: HttpRequestExtractor<S> + 'static, T2: HttpRequestExtractor<S> + 'static,
S: 'static S: 'static
{ {
started: bool,
hnd: Rc<UnsafeCell<F>>, hnd: Rc<UnsafeCell<F>>,
req: HttpRequest<S>, req: HttpRequest<S>,
item: Option<T1>, item: Option<T1>,
@ -251,6 +218,45 @@ impl<T1, T2, S, F, R> Future for WithHandlerFut2<T1, T2, S, F, R>
return fut.poll() return fut.poll()
} }
if !self.started {
self.started = true;
let mut fut = T1::extract(&self.req);
match fut.poll() {
Ok(Async::Ready(item1)) => {
let mut fut = T2::extract(&self.req);
match fut.poll() {
Ok(Async::Ready(item2)) => {
let hnd: &mut F = unsafe{&mut *self.hnd.get()};
match (*hnd)(item1, item2)
.respond_to(self.req.without_state())
{
Ok(item) => match item.into().into() {
ReplyItem::Message(resp) =>
return Ok(Async::Ready(resp)),
ReplyItem::Future(fut) => {
self.fut3 = Some(fut);
return self.poll()
}
},
Err(e) => return Err(e.into()),
}
},
Ok(Async::NotReady) => {
self.item = Some(item1);
self.fut2 = Some(Box::new(fut));
return Ok(Async::NotReady);
},
Err(e) => return Err(e),
}
},
Ok(Async::NotReady) => {
self.fut1 = Some(Box::new(fut));
return Ok(Async::NotReady);
}
Err(e) => return Err(e),
}
}
if self.fut1.is_some() { if self.fut1.is_some() {
match self.fut1.as_mut().unwrap().poll()? { match self.fut1.as_mut().unwrap().poll()? {
Async::Ready(item) => { Async::Ready(item) => {
@ -322,19 +328,22 @@ impl<T1, T2, T3, S, F, R> Handler<S> for With3<T1, T2, T3, S, F, R>
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(T1::extract(&req)); let mut fut = WithHandlerFut3{
req,
Reply::async( hnd: Rc::clone(&self.hnd),
WithHandlerFut3{ started: false,
req, item1: None,
hnd: Rc::clone(&self.hnd), item2: None,
item1: None, fut1: None,
item2: None, fut2: None,
fut1: Some(fut), fut3: None,
fut2: None, fut4: None,
fut3: None, };
fut4: None, match fut.poll() {
}) Ok(Async::Ready(resp)) => Reply::response(resp),
Ok(Async::NotReady) => Reply::async(fut),
Err(e) => Reply::response(e),
}
} }
} }
@ -348,6 +357,7 @@ struct WithHandlerFut3<T1, T2, T3, S, F, R>
{ {
hnd: Rc<UnsafeCell<F>>, hnd: Rc<UnsafeCell<F>>,
req: HttpRequest<S>, req: HttpRequest<S>,
started: bool,
item1: Option<T1>, item1: Option<T1>,
item2: Option<T2>, item2: Option<T2>,
fut1: Option<Box<Future<Item=T1, Error=Error>>>, fut1: Option<Box<Future<Item=T1, Error=Error>>>,
@ -372,6 +382,57 @@ impl<T1, T2, T3, S, F, R> Future for WithHandlerFut3<T1, T2, T3, S, F, R>
return fut.poll() return fut.poll()
} }
if !self.started {
self.started = true;
let mut fut = T1::extract(&self.req);
match fut.poll() {
Ok(Async::Ready(item1)) => {
let mut fut = T2::extract(&self.req);
match fut.poll() {
Ok(Async::Ready(item2)) => {
let mut fut = T3::extract(&self.req);
match fut.poll() {
Ok(Async::Ready(item3)) => {
let hnd: &mut F = unsafe{&mut *self.hnd.get()};
match (*hnd)(item1, item2, item3)
.respond_to(self.req.without_state())
{
Ok(item) => match item.into().into() {
ReplyItem::Message(resp) =>
return Ok(Async::Ready(resp)),
ReplyItem::Future(fut) => {
self.fut4 = Some(fut);
return self.poll()
}
},
Err(e) => return Err(e.into()),
}
},
Ok(Async::NotReady) => {
self.item1 = Some(item1);
self.item2 = Some(item2);
self.fut3 = Some(Box::new(fut));
return Ok(Async::NotReady);
},
Err(e) => return Err(e),
}
},
Ok(Async::NotReady) => {
self.item1 = Some(item1);
self.fut2 = Some(Box::new(fut));
return Ok(Async::NotReady);
},
Err(e) => return Err(e),
}
},
Ok(Async::NotReady) => {
self.fut1 = Some(Box::new(fut));
return Ok(Async::NotReady);
}
Err(e) => return Err(e),
}
}
if self.fut1.is_some() { if self.fut1.is_some() {
match self.fut1.as_mut().unwrap().poll()? { match self.fut1.as_mut().unwrap().poll()? {
Async::Ready(item) => { Async::Ready(item) => {