mirror of
https://github.com/actix/actix-web.git
synced 2024-11-22 17:41:11 +00:00
drop request's extract_xxx methods
This commit is contained in:
parent
9e61c67128
commit
7d6deab9fb
5 changed files with 172 additions and 199 deletions
|
@ -2,9 +2,7 @@
|
|||
|
||||
## 0.4.11
|
||||
|
||||
* Added `Route::with()` handler, uses request extractor
|
||||
|
||||
* Added `HttpReuqest::extract_xxx()`, type safe path/query information extractor
|
||||
* Type-safe path/query parameter handling, using serde #70
|
||||
|
||||
* Router cannot parse Non-ASCII characters in URL #137
|
||||
|
||||
|
|
|
@ -305,6 +305,8 @@ impl<'de, S: 'de> Deserializer<'de> for PathExtractor<'de, S>
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures::Async;
|
||||
use super::*;
|
||||
use router::{Router, Pattern};
|
||||
use resource::Resource;
|
||||
use test::TestRequest;
|
||||
|
@ -332,15 +334,27 @@ mod tests {
|
|||
let (router, _) = Router::new("", ServerSettings::default(), routes);
|
||||
assert!(router.recognize(&mut req).is_some());
|
||||
|
||||
let s: MyStruct = req.extract_path().unwrap();
|
||||
assert_eq!(s.key, "name");
|
||||
assert_eq!(s.value, "user1");
|
||||
match Path::<MyStruct, _>::extract(&req).poll().unwrap() {
|
||||
Async::Ready(s) => {
|
||||
assert_eq!(s.key, "name");
|
||||
assert_eq!(s.value, "user1");
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let s: (String, String) = req.extract_path().unwrap();
|
||||
assert_eq!(s.0, "name");
|
||||
assert_eq!(s.1, "user1");
|
||||
match Path::<(String, String), _>::extract(&req).poll().unwrap() {
|
||||
Async::Ready(s) => {
|
||||
assert_eq!(s.0, "name");
|
||||
assert_eq!(s.1, "user1");
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let s: Id = req.extract_query().unwrap();
|
||||
assert_eq!(s.id, "test");
|
||||
match Query::<Id, _>::extract(&req).poll().unwrap() {
|
||||
Async::Ready(s) => {
|
||||
assert_eq!(s.id, "test");
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,14 +155,6 @@ impl Reply {
|
|||
_ => 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 {
|
||||
|
|
|
@ -5,13 +5,12 @@ use std::net::SocketAddr;
|
|||
use std::borrow::Cow;
|
||||
use bytes::Bytes;
|
||||
use cookie::Cookie;
|
||||
use futures::{Async, Future, Stream, Poll};
|
||||
use futures::{Async, Stream, Poll};
|
||||
use futures_cpupool::CpuPool;
|
||||
use failure;
|
||||
use url::{Url, form_urlencoded};
|
||||
use http::{header, Uri, Method, Version, HeaderMap, Extensions, StatusCode};
|
||||
use tokio_io::AsyncRead;
|
||||
use serde::de;
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use body::Body;
|
||||
|
@ -22,8 +21,7 @@ use payload::Payload;
|
|||
use httpmessage::HttpMessage;
|
||||
use httpresponse::{HttpResponse, HttpResponseBuilder};
|
||||
use helpers::SharedHttpInnerMessage;
|
||||
use extractor::{Path, Query, HttpRequestExtractor};
|
||||
use error::{Error, UrlGenerationError, CookieParseError, PayloadError};
|
||||
use error::{UrlGenerationError, CookieParseError, PayloadError};
|
||||
|
||||
|
||||
pub struct HttpInnerMessage {
|
||||
|
@ -406,96 +404,6 @@ impl<S> HttpRequest<S> {
|
|||
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.
|
||||
pub fn keep_alive(&self) -> bool {
|
||||
self.as_ref().keep_alive()
|
||||
|
|
233
src/with.rs
233
src/with.rs
|
@ -62,31 +62,17 @@ impl<T, S, H> Handler<S> for With<T, S, H>
|
|||
type Result = Reply;
|
||||
|
||||
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() {
|
||||
Ok(Async::Ready(item)) => {
|
||||
let hnd: &mut H = unsafe{&mut *self.hnd.get()};
|
||||
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,
|
||||
}),
|
||||
Ok(Async::Ready(resp)) => Reply::response(resp),
|
||||
Ok(Async::NotReady) => Reply::async(fut),
|
||||
Err(e) => Reply::response(e),
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +83,7 @@ struct WithHandlerFut<T, S, H>
|
|||
T: HttpRequestExtractor<S>,
|
||||
T: 'static, S: 'static
|
||||
{
|
||||
started: bool,
|
||||
hnd: Rc<UnsafeCell<H>>,
|
||||
req: HttpRequest<S>,
|
||||
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()
|
||||
}
|
||||
|
||||
let item = match self.fut1.as_mut().unwrap().poll()? {
|
||||
Async::Ready(item) => item,
|
||||
Async::NotReady => return Ok(Async::NotReady),
|
||||
let item = if !self.started {
|
||||
self.started = true;
|
||||
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 item = match hnd.handle(item)
|
||||
.respond_to(self.req.without_state())
|
||||
{
|
||||
let item = match hnd.handle(item).respond_to(self.req.without_state()) {
|
||||
Ok(item) => item.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;
|
||||
|
||||
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() {
|
||||
Ok(Async::Ready(item1)) => {
|
||||
let mut fut = T2::extract(&req);
|
||||
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,
|
||||
}),
|
||||
Ok(Async::Ready(resp)) => Reply::response(resp),
|
||||
Ok(Async::NotReady) => Reply::async(fut),
|
||||
Err(e) => Reply::response(e),
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +194,7 @@ struct WithHandlerFut2<T1, T2, S, F, R>
|
|||
T2: HttpRequestExtractor<S> + 'static,
|
||||
S: 'static
|
||||
{
|
||||
started: bool,
|
||||
hnd: Rc<UnsafeCell<F>>,
|
||||
req: HttpRequest<S>,
|
||||
item: Option<T1>,
|
||||
|
@ -251,6 +218,45 @@ impl<T1, T2, S, F, R> Future for WithHandlerFut2<T1, T2, S, F, R>
|
|||
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() {
|
||||
match self.fut1.as_mut().unwrap().poll()? {
|
||||
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;
|
||||
|
||||
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,
|
||||
})
|
||||
let mut fut = WithHandlerFut3{
|
||||
req,
|
||||
hnd: Rc::clone(&self.hnd),
|
||||
started: false,
|
||||
item1: None,
|
||||
item2: None,
|
||||
fut1: None,
|
||||
fut2: 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>>,
|
||||
req: HttpRequest<S>,
|
||||
started: bool,
|
||||
item1: Option<T1>,
|
||||
item2: Option<T2>,
|
||||
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()
|
||||
}
|
||||
|
||||
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() {
|
||||
match self.fut1.as_mut().unwrap().poll()? {
|
||||
Async::Ready(item) => {
|
||||
|
|
Loading…
Reference in a new issue