actix_web/
extract.rs

1//! Request extractors
2
3use std::{
4    convert::Infallible,
5    future::Future,
6    marker::PhantomData,
7    pin::Pin,
8    task::{Context, Poll},
9};
10
11use actix_http::{Method, Uri};
12use actix_utils::future::{ok, Ready};
13use futures_core::ready;
14use pin_project_lite::pin_project;
15
16use crate::{dev::Payload, Error, HttpRequest};
17
18/// A type that implements [`FromRequest`] is called an **extractor** and can extract data from
19/// the request. Some types that implement this trait are: [`Json`], [`Header`], and [`Path`].
20///
21/// Check out [`ServiceRequest::extract`](crate::dev::ServiceRequest::extract) if you want to
22/// leverage extractors when implementing middlewares.
23///
24/// # Configuration
25/// An extractor can be customized by injecting the corresponding configuration with one of:
26/// - [`App::app_data()`][crate::App::app_data]
27/// - [`Scope::app_data()`][crate::Scope::app_data]
28/// - [`Resource::app_data()`][crate::Resource::app_data]
29///
30/// Here are some built-in extractors and their corresponding configuration.
31/// Please refer to the respective documentation for details.
32///
33/// | Extractor   | Configuration     |
34/// |-------------|-------------------|
35/// | [`Header`]  | _None_            |
36/// | [`Path`]    | [`PathConfig`]    |
37/// | [`Json`]    | [`JsonConfig`]    |
38/// | [`Form`]    | [`FormConfig`]    |
39/// | [`Query`]   | [`QueryConfig`]   |
40/// | [`Bytes`]   | [`PayloadConfig`] |
41/// | [`String`]  | [`PayloadConfig`] |
42/// | [`Payload`] | [`PayloadConfig`] |
43///
44/// # Implementing An Extractor
45/// To reduce duplicate code in handlers where extracting certain parts of a request has a common
46/// structure, you can implement `FromRequest` for your own types.
47///
48/// Note that the request payload can only be consumed by one extractor.
49///
50/// [`Header`]: crate::web::Header
51/// [`Json`]: crate::web::Json
52/// [`JsonConfig`]: crate::web::JsonConfig
53/// [`Form`]: crate::web::Form
54/// [`FormConfig`]: crate::web::FormConfig
55/// [`Path`]: crate::web::Path
56/// [`PathConfig`]: crate::web::PathConfig
57/// [`Query`]: crate::web::Query
58/// [`QueryConfig`]: crate::web::QueryConfig
59/// [`Payload`]: crate::web::Payload
60/// [`PayloadConfig`]: crate::web::PayloadConfig
61/// [`String`]: FromRequest#impl-FromRequest-for-String
62/// [`Bytes`]: crate::web::Bytes#impl-FromRequest
63/// [`Either`]: crate::web::Either
64#[doc(alias = "extract", alias = "extractor")]
65pub trait FromRequest: Sized {
66    /// The associated error which can be returned.
67    type Error: Into<Error>;
68
69    /// Future that resolves to a `Self`.
70    ///
71    /// To use an async function or block, the futures must be boxed. The following snippet will be
72    /// common when creating async/await extractors (that do not consume the body).
73    ///
74    /// ```ignore
75    /// type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
76    /// // or
77    /// type Future = futures_util::future::LocalBoxFuture<'static, Result<Self, Self::Error>>;
78    ///
79    /// fn from_request(req: HttpRequest, ...) -> Self::Future {
80    ///     let req = req.clone();
81    ///     Box::pin(async move {
82    ///         ...
83    ///     })
84    /// }
85    /// ```
86    type Future: Future<Output = Result<Self, Self::Error>>;
87
88    /// Create a `Self` from request parts asynchronously.
89    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future;
90
91    /// Create a `Self` from request head asynchronously.
92    ///
93    /// This method is short for `T::from_request(req, &mut Payload::None)`.
94    fn extract(req: &HttpRequest) -> Self::Future {
95        Self::from_request(req, &mut Payload::None)
96    }
97}
98
99/// Optionally extract from the request.
100///
101/// If the inner `T::from_request` returns an error, handler will receive `None` instead.
102///
103/// # Examples
104/// ```
105/// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};
106/// use actix_web::error::ErrorBadRequest;
107/// use futures_util::future::{ok, err, Ready};
108/// use serde::Deserialize;
109/// use rand;
110///
111/// #[derive(Debug, Deserialize)]
112/// struct Thing {
113///     name: String
114/// }
115///
116/// impl FromRequest for Thing {
117///     type Error = Error;
118///     type Future = Ready<Result<Self, Self::Error>>;
119///
120///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
121///         if rand::random() {
122///             ok(Thing { name: "thingy".into() })
123///         } else {
124///             err(ErrorBadRequest("no luck"))
125///         }
126///
127///     }
128/// }
129///
130/// /// extract `Thing` from request
131/// async fn index(supplied_thing: Option<Thing>) -> String {
132///     match supplied_thing {
133///         // Puns not intended
134///         Some(thing) => format!("Got something: {:?}", thing),
135///         None => format!("No thing!")
136///     }
137/// }
138///
139/// let app = App::new().service(
140///     web::resource("/users/:first").route(
141///         web::post().to(index))
142/// );
143/// ```
144impl<T> FromRequest for Option<T>
145where
146    T: FromRequest,
147{
148    type Error = Infallible;
149    type Future = FromRequestOptFuture<T::Future>;
150
151    #[inline]
152    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
153        FromRequestOptFuture {
154            fut: T::from_request(req, payload),
155        }
156    }
157}
158
159pin_project! {
160    pub struct FromRequestOptFuture<Fut> {
161        #[pin]
162        fut: Fut,
163    }
164}
165
166impl<Fut, T, E> Future for FromRequestOptFuture<Fut>
167where
168    Fut: Future<Output = Result<T, E>>,
169    E: Into<Error>,
170{
171    type Output = Result<Option<T>, Infallible>;
172
173    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
174        let this = self.project();
175        let res = ready!(this.fut.poll(cx));
176        match res {
177            Ok(t) => Poll::Ready(Ok(Some(t))),
178            Err(err) => {
179                log::debug!("Error for Option<T> extractor: {}", err.into());
180                Poll::Ready(Ok(None))
181            }
182        }
183    }
184}
185
186/// Extract from the request, passing error type through to handler.
187///
188/// If the inner `T::from_request` returns an error, allow handler to receive the error rather than
189/// immediately returning an error response.
190///
191/// # Examples
192/// ```
193/// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};
194/// use actix_web::error::ErrorBadRequest;
195/// use futures_util::future::{ok, err, Ready};
196/// use serde::Deserialize;
197/// use rand;
198///
199/// #[derive(Debug, Deserialize)]
200/// struct Thing {
201///     name: String
202/// }
203///
204/// impl FromRequest for Thing {
205///     type Error = Error;
206///     type Future = Ready<Result<Thing, Error>>;
207///
208///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
209///         if rand::random() {
210///             ok(Thing { name: "thingy".into() })
211///         } else {
212///             err(ErrorBadRequest("no luck"))
213///         }
214///     }
215/// }
216///
217/// /// extract `Thing` from request
218/// async fn index(supplied_thing: Result<Thing>) -> String {
219///     match supplied_thing {
220///         Ok(thing) => format!("Got thing: {thing:?}"),
221///         Err(err) => format!("Error extracting thing: {err}"),
222///     }
223/// }
224///
225/// let app = App::new().service(
226///     web::resource("/users/:first").route(web::post().to(index))
227/// );
228/// ```
229impl<T, E> FromRequest for Result<T, E>
230where
231    T: FromRequest,
232    T::Error: Into<E>,
233{
234    type Error = Infallible;
235    type Future = FromRequestResFuture<T::Future, E>;
236
237    #[inline]
238    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
239        FromRequestResFuture {
240            fut: T::from_request(req, payload),
241            _phantom: PhantomData,
242        }
243    }
244}
245
246pin_project! {
247    pub struct FromRequestResFuture<Fut, E> {
248        #[pin]
249        fut: Fut,
250        _phantom: PhantomData<E>,
251    }
252}
253
254impl<Fut, T, Ei, E> Future for FromRequestResFuture<Fut, E>
255where
256    Fut: Future<Output = Result<T, Ei>>,
257    Ei: Into<E>,
258{
259    type Output = Result<Result<T, E>, Infallible>;
260
261    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
262        let this = self.project();
263        let res = ready!(this.fut.poll(cx));
264        Poll::Ready(Ok(res.map_err(Into::into)))
265    }
266}
267
268/// Extract the request's URI.
269///
270/// # Examples
271/// ```
272/// use actix_web::{http::Uri, web, App, Responder};
273///
274/// async fn handler(uri: Uri) -> impl Responder {
275///     format!("Requested path: {}", uri.path())
276/// }
277///
278/// let app = App::new().default_service(web::to(handler));
279/// ```
280impl FromRequest for Uri {
281    type Error = Infallible;
282    type Future = Ready<Result<Self, Self::Error>>;
283
284    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
285        ok(req.uri().clone())
286    }
287}
288
289/// Extract the request's method.
290///
291/// # Examples
292/// ```
293/// use actix_web::{http::Method, web, App, Responder};
294///
295/// async fn handler(method: Method) -> impl Responder {
296///     format!("Request method: {}", method)
297/// }
298///
299/// let app = App::new().default_service(web::to(handler));
300/// ```
301impl FromRequest for Method {
302    type Error = Infallible;
303    type Future = Ready<Result<Self, Self::Error>>;
304
305    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
306        ok(req.method().clone())
307    }
308}
309
310#[doc(hidden)]
311#[allow(non_snake_case)]
312mod tuple_from_req {
313    use super::*;
314
315    macro_rules! tuple_from_req {
316        ($fut: ident; $($T: ident),*) => {
317            /// FromRequest implementation for tuple
318            #[allow(unused_parens)]
319            impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)
320            {
321                type Error = Error;
322                type Future = $fut<$($T),+>;
323
324                fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
325                    $fut {
326                        $(
327                            $T: ExtractFuture::Future {
328                                fut: $T::from_request(req, payload)
329                            },
330                        )+
331                    }
332                }
333            }
334
335            pin_project! {
336                pub struct $fut<$($T: FromRequest),+> {
337                    $(
338                        #[pin]
339                        $T: ExtractFuture<$T::Future, $T>,
340                    )+
341                }
342            }
343
344            impl<$($T: FromRequest),+> Future for $fut<$($T),+>
345            {
346                type Output = Result<($($T,)+), Error>;
347
348                fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
349                    let mut this = self.project();
350
351                    let mut ready = true;
352                    $(
353                        match this.$T.as_mut().project() {
354                            ExtractProj::Future { fut } => match fut.poll(cx) {
355                                Poll::Ready(Ok(output)) => {
356                                    let _ = this.$T.as_mut().project_replace(ExtractFuture::Done { output });
357                                },
358                                Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())),
359                                Poll::Pending => ready = false,
360                            },
361                            ExtractProj::Done { .. } => {},
362                            ExtractProj::Empty => unreachable!("FromRequest polled after finished"),
363                        }
364                    )+
365
366                    if ready {
367                        Poll::Ready(Ok(
368                            ($(
369                                match this.$T.project_replace(ExtractFuture::Empty) {
370                                    ExtractReplaceProj::Done { output } => output,
371                                    _ => unreachable!("FromRequest polled after finished"),
372                                },
373                            )+)
374                        ))
375                    } else {
376                        Poll::Pending
377                    }
378                }
379            }
380        };
381    }
382
383    pin_project! {
384        #[project = ExtractProj]
385        #[project_replace = ExtractReplaceProj]
386        enum ExtractFuture<Fut, Res> {
387            Future {
388                #[pin]
389                fut: Fut
390            },
391            Done {
392                output: Res,
393            },
394            Empty
395        }
396    }
397
398    impl FromRequest for () {
399        type Error = Infallible;
400        type Future = Ready<Result<Self, Self::Error>>;
401
402        fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
403            ok(())
404        }
405    }
406
407    tuple_from_req! { TupleFromRequest1; A }
408    tuple_from_req! { TupleFromRequest2; A, B }
409    tuple_from_req! { TupleFromRequest3; A, B, C }
410    tuple_from_req! { TupleFromRequest4; A, B, C, D }
411    tuple_from_req! { TupleFromRequest5; A, B, C, D, E }
412    tuple_from_req! { TupleFromRequest6; A, B, C, D, E, F }
413    tuple_from_req! { TupleFromRequest7; A, B, C, D, E, F, G }
414    tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H }
415    tuple_from_req! { TupleFromRequest9; A, B, C, D, E, F, G, H, I }
416    tuple_from_req! { TupleFromRequest10; A, B, C, D, E, F, G, H, I, J }
417    tuple_from_req! { TupleFromRequest11; A, B, C, D, E, F, G, H, I, J, K }
418    tuple_from_req! { TupleFromRequest12; A, B, C, D, E, F, G, H, I, J, K, L }
419    tuple_from_req! { TupleFromRequest13; A, B, C, D, E, F, G, H, I, J, K, L, M }
420    tuple_from_req! { TupleFromRequest14; A, B, C, D, E, F, G, H, I, J, K, L, M, N }
421    tuple_from_req! { TupleFromRequest15; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O }
422    tuple_from_req! { TupleFromRequest16; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P }
423}
424
425#[cfg(test)]
426mod tests {
427    use actix_http::header;
428    use bytes::Bytes;
429    use serde::Deserialize;
430
431    use super::*;
432    use crate::{
433        test::TestRequest,
434        types::{Form, FormConfig},
435    };
436
437    #[derive(Deserialize, Debug, PartialEq)]
438    struct Info {
439        hello: String,
440    }
441
442    #[actix_rt::test]
443    async fn test_option() {
444        let (req, mut pl) = TestRequest::default()
445            .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
446            .data(FormConfig::default().limit(4096))
447            .to_http_parts();
448
449        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
450            .await
451            .unwrap();
452        assert_eq!(r, None);
453
454        let (req, mut pl) = TestRequest::default()
455            .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
456            .insert_header((header::CONTENT_LENGTH, "9"))
457            .set_payload(Bytes::from_static(b"hello=world"))
458            .to_http_parts();
459
460        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
461            .await
462            .unwrap();
463        assert_eq!(
464            r,
465            Some(Form(Info {
466                hello: "world".into()
467            }))
468        );
469
470        let (req, mut pl) = TestRequest::default()
471            .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
472            .insert_header((header::CONTENT_LENGTH, "9"))
473            .set_payload(Bytes::from_static(b"bye=world"))
474            .to_http_parts();
475
476        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
477            .await
478            .unwrap();
479        assert_eq!(r, None);
480    }
481
482    #[actix_rt::test]
483    async fn test_result() {
484        let (req, mut pl) = TestRequest::default()
485            .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
486            .insert_header((header::CONTENT_LENGTH, "11"))
487            .set_payload(Bytes::from_static(b"hello=world"))
488            .to_http_parts();
489
490        let r = Result::<Form<Info>, Error>::from_request(&req, &mut pl)
491            .await
492            .unwrap()
493            .unwrap();
494        assert_eq!(
495            r,
496            Form(Info {
497                hello: "world".into()
498            })
499        );
500
501        let (req, mut pl) = TestRequest::default()
502            .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
503            .insert_header((header::CONTENT_LENGTH, 9))
504            .set_payload(Bytes::from_static(b"bye=world"))
505            .to_http_parts();
506
507        struct MyError;
508        impl From<Error> for MyError {
509            fn from(_: Error) -> Self {
510                Self
511            }
512        }
513
514        let r = Result::<Form<Info>, MyError>::from_request(&req, &mut pl)
515            .await
516            .unwrap();
517        assert!(r.is_err());
518    }
519
520    #[actix_rt::test]
521    async fn test_uri() {
522        let req = TestRequest::default().uri("/foo/bar").to_http_request();
523        let uri = Uri::extract(&req).await.unwrap();
524        assert_eq!(uri.path(), "/foo/bar");
525    }
526
527    #[actix_rt::test]
528    async fn test_method() {
529        let req = TestRequest::default().method(Method::GET).to_http_request();
530        let method = Method::extract(&req).await.unwrap();
531        assert_eq!(method, Method::GET);
532    }
533
534    #[actix_rt::test]
535    async fn test_concurrent() {
536        let (req, mut pl) = TestRequest::default()
537            .uri("/foo/bar")
538            .method(Method::GET)
539            .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
540            .insert_header((header::CONTENT_LENGTH, "11"))
541            .set_payload(Bytes::from_static(b"hello=world"))
542            .to_http_parts();
543        let (method, uri, form) = <(Method, Uri, Form<Info>)>::from_request(&req, &mut pl)
544            .await
545            .unwrap();
546        assert_eq!(method, Method::GET);
547        assert_eq!(uri.path(), "/foo/bar");
548        assert_eq!(
549            form,
550            Form(Info {
551                hello: "world".into()
552            })
553        );
554    }
555}