Skip to main content

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