//! Request extractors use actix_http::error::Error; use futures::future::ok; use futures::{future, Async, Future, IntoFuture, Poll}; use crate::dev::Payload; use crate::request::HttpRequest; /// Trait implemented by types that can be extracted from request. /// /// Types that implement this trait can be used with `Route` handlers. pub trait FromRequest: Sized { /// The associated error which can be returned. type Error: Into; /// Future that resolves to a Self type Future: IntoFuture; /// Convert request to a Self fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future; /// Convert request to a Self /// /// This method uses `Payload::None` as payload stream. fn extract(req: &HttpRequest) -> Self::Future { Self::from_request(req, &mut Payload::None) } } /// Optionally extract a field from the request /// /// If the FromRequest for T fails, return None rather than returning an error response /// /// ## Example /// /// ```rust /// # #[macro_use] extern crate serde_derive; /// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; /// use rand; /// /// #[derive(Debug, Deserialize)] /// struct Thing { /// name: String /// } /// /// impl FromRequest for Thing { /// type Error = Error; /// type Future = Result; /// /// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { /// if rand::random() { /// Ok(Thing { name: "thingy".into() }) /// } else { /// Err(ErrorBadRequest("no luck")) /// } /// /// } /// } /// /// /// extract `Thing` from request /// fn index(supplied_thing: Option) -> String { /// match supplied_thing { /// // Puns not intended /// Some(thing) => format!("Got something: {:?}", thing), /// None => format!("No thing!") /// } /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/users/:first").route( /// web::post().to(index)) /// ); /// } /// ``` impl FromRequest for Option where T: FromRequest, T::Future: 'static, { type Error = Error; type Future = Box, Error = Error>>; #[inline] fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { Box::new( T::from_request(req, payload) .into_future() .then(|r| match r { Ok(v) => future::ok(Some(v)), Err(e) => { log::debug!("Error for Option extractor: {}", e.into()); future::ok(None) } }), ) } } /// Optionally extract a field from the request or extract the Error if unsuccessful /// /// If the `FromRequest` for T fails, inject Err into handler rather than returning an error response /// /// ## Example /// /// ```rust /// # #[macro_use] extern crate serde_derive; /// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; /// use rand; /// /// #[derive(Debug, Deserialize)] /// struct Thing { /// name: String /// } /// /// impl FromRequest for Thing { /// type Error = Error; /// type Future = Result; /// /// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { /// if rand::random() { /// Ok(Thing { name: "thingy".into() }) /// } else { /// Err(ErrorBadRequest("no luck")) /// } /// } /// } /// /// /// extract `Thing` from request /// fn index(supplied_thing: Result) -> String { /// match supplied_thing { /// Ok(thing) => format!("Got thing: {:?}", thing), /// Err(e) => format!("Error extracting thing: {}", e) /// } /// } /// /// fn main() { /// let app = App::new().service( /// web::resource("/users/:first").route(web::post().to(index)) /// ); /// } /// ``` impl FromRequest for Result where T: FromRequest, T::Future: 'static, T::Error: 'static, { type Error = Error; type Future = Box, Error = Error>>; #[inline] fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { Box::new( T::from_request(req, payload) .into_future() .then(|res| match res { Ok(v) => ok(Ok(v)), Err(e) => ok(Err(e)), }), ) } } #[doc(hidden)] impl FromRequest for () { type Error = Error; type Future = Result<(), Error>; fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future { Ok(()) } } macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { /// FromRequest implementation for tuple #[doc(hidden)] impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+) { type Error = Error; type Future = $fut_type<$($T),+>; fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { $fut_type { items: <($(Option<$T>,)+)>::default(), futs: ($($T::from_request(req, payload).into_future(),)+), } } } #[doc(hidden)] pub struct $fut_type<$($T: FromRequest),+> { items: ($(Option<$T>,)+), futs: ($(<$T::Future as futures::IntoFuture>::Future,)+), } impl<$($T: FromRequest),+> Future for $fut_type<$($T),+> { type Item = ($($T,)+); type Error = Error; fn poll(&mut self) -> Poll { let mut ready = true; $( if self.items.$n.is_none() { match self.futs.$n.poll() { Ok(Async::Ready(item)) => { self.items.$n = Some(item); } Ok(Async::NotReady) => ready = false, Err(e) => return Err(e.into()), } } )+ if ready { Ok(Async::Ready( ($(self.items.$n.take().unwrap(),)+) )) } else { Ok(Async::NotReady) } } } }); #[rustfmt::skip] mod m { use super::*; tuple_from_req!(TupleFromRequest1, (0, A)); tuple_from_req!(TupleFromRequest2, (0, A), (1, B)); tuple_from_req!(TupleFromRequest3, (0, A), (1, B), (2, C)); tuple_from_req!(TupleFromRequest4, (0, A), (1, B), (2, C), (3, D)); tuple_from_req!(TupleFromRequest5, (0, A), (1, B), (2, C), (3, D), (4, E)); tuple_from_req!(TupleFromRequest6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F)); tuple_from_req!(TupleFromRequest7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G)); tuple_from_req!(TupleFromRequest8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H)); tuple_from_req!(TupleFromRequest9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I)); tuple_from_req!(TupleFromRequest10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J)); } #[cfg(test)] mod tests { use actix_http::http::header; use actix_router::ResourceDef; use bytes::Bytes; use serde_derive::Deserialize; use super::*; use crate::test::{block_on, TestRequest}; use crate::types::{Form, FormConfig, Path, Query}; #[derive(Deserialize, Debug, PartialEq)] struct Info { hello: String, } #[test] fn test_option() { let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", ) .route_data(FormConfig::default().limit(4096)) .to_http_parts(); let r = block_on(Option::>::from_request(&req, &mut pl)).unwrap(); assert_eq!(r, None); let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", ) .header(header::CONTENT_LENGTH, "9") .set_payload(Bytes::from_static(b"hello=world")) .to_http_parts(); let r = block_on(Option::>::from_request(&req, &mut pl)).unwrap(); assert_eq!( r, Some(Form(Info { hello: "world".into() })) ); let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", ) .header(header::CONTENT_LENGTH, "9") .set_payload(Bytes::from_static(b"bye=world")) .to_http_parts(); let r = block_on(Option::>::from_request(&req, &mut pl)).unwrap(); assert_eq!(r, None); } #[test] fn test_result() { let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", ) .header(header::CONTENT_LENGTH, "11") .set_payload(Bytes::from_static(b"hello=world")) .to_http_parts(); let r = block_on(Result::, Error>::from_request(&req, &mut pl)) .unwrap() .unwrap(); assert_eq!( r, Form(Info { hello: "world".into() }) ); let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", ) .header(header::CONTENT_LENGTH, "9") .set_payload(Bytes::from_static(b"bye=world")) .to_http_parts(); let r = block_on(Result::, Error>::from_request(&req, &mut pl)).unwrap(); assert!(r.is_err()); } #[derive(Deserialize)] struct MyStruct { key: String, value: String, } #[derive(Deserialize)] struct Id { id: String, } #[derive(Deserialize)] struct Test2 { key: String, value: u32, } #[test] fn test_request_extract() { let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); let resource = ResourceDef::new("/{key}/{value}/"); resource.match_path(req.match_info_mut()); let (req, mut pl) = req.into_parts(); let s = Path::::from_request(&req, &mut pl).unwrap(); assert_eq!(s.key, "name"); assert_eq!(s.value, "user1"); let s = Path::<(String, String)>::from_request(&req, &mut pl).unwrap(); assert_eq!(s.0, "name"); assert_eq!(s.1, "user1"); let s = Query::::from_request(&req, &mut pl).unwrap(); assert_eq!(s.id, "test"); let mut req = TestRequest::with_uri("/name/32/").to_srv_request(); let resource = ResourceDef::new("/{key}/{value}/"); resource.match_path(req.match_info_mut()); let (req, mut pl) = req.into_parts(); let s = Path::::from_request(&req, &mut pl).unwrap(); assert_eq!(s.as_ref().key, "name"); assert_eq!(s.value, 32); let s = Path::<(String, u8)>::from_request(&req, &mut pl).unwrap(); assert_eq!(s.0, "name"); assert_eq!(s.1, 32); let res = Path::>::from_request(&req, &mut pl).unwrap(); assert_eq!(res[0], "name".to_owned()); assert_eq!(res[1], "32".to_owned()); } }