actix_web/request_data.rs
1use std::{any::type_name, ops::Deref};
2
3use actix_utils::future::{err, ok, Ready};
4
5use crate::{
6 dev::Payload, error::ErrorInternalServerError, Error, FromRequest, HttpMessage as _,
7 HttpRequest,
8};
9
10/// Request-local data extractor.
11///
12/// Request-local data is arbitrary data attached to an individual request, usually
13/// by middleware. It can be set via `extensions_mut` on [`HttpRequest`][htr_ext_mut]
14/// or [`ServiceRequest`][srv_ext_mut].
15///
16/// Unlike app data, request data is dropped when the request has finished processing. This makes it
17/// useful as a kind of messaging system between middleware and request handlers. It uses the same
18/// types-as-keys storage system as app data.
19///
20/// # Mutating Request Data
21/// Note that since extractors must output owned data, only types that `impl Clone` can use this
22/// extractor. A clone is taken of the required request data and can, therefore, not be directly
23/// mutated in-place. To mutate request data, continue to use [`HttpRequest::extensions_mut`] or
24/// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not
25/// provided to make this potential foot-gun more obvious.
26///
27/// # Examples
28/// ```no_run
29/// # use actix_web::{web, HttpResponse, HttpRequest, Responder, HttpMessage as _};
30/// #[derive(Debug, Clone, PartialEq)]
31/// struct FlagFromMiddleware(String);
32///
33/// /// Use the `ReqData<T>` extractor to access request data in a handler.
34/// async fn handler(
35/// req: HttpRequest,
36/// opt_flag: Option<web::ReqData<FlagFromMiddleware>>,
37/// ) -> impl Responder {
38/// // use an option extractor if middleware is not guaranteed to add this type of req data
39/// if let Some(flag) = opt_flag {
40/// assert_eq!(&flag.into_inner(), req.extensions().get::<FlagFromMiddleware>().unwrap());
41/// }
42///
43/// HttpResponse::Ok()
44/// }
45/// ```
46///
47/// [htr_ext_mut]: crate::HttpRequest::extensions_mut
48/// [srv_ext_mut]: crate::dev::ServiceRequest::extensions_mut
49#[derive(Debug, Clone)]
50pub struct ReqData<T: Clone + 'static>(T);
51
52impl<T: Clone + 'static> ReqData<T> {
53 /// Consumes the `ReqData`, returning its wrapped data.
54 pub fn into_inner(self) -> T {
55 self.0
56 }
57}
58
59impl<T: Clone + 'static> Deref for ReqData<T> {
60 type Target = T;
61
62 fn deref(&self) -> &T {
63 &self.0
64 }
65}
66
67impl<T: Clone + 'static> FromRequest for ReqData<T> {
68 type Error = Error;
69 type Future = Ready<Result<Self, Error>>;
70
71 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
72 if let Some(st) = req.extensions().get::<T>() {
73 ok(ReqData(st.clone()))
74 } else {
75 log::debug!(
76 "Failed to construct App-level ReqData extractor. \
77 Request path: {:?} (type: {})",
78 req.path(),
79 type_name::<T>(),
80 );
81 err(ErrorInternalServerError(
82 "Missing expected request extension data",
83 ))
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use std::{cell::RefCell, rc::Rc};
91
92 use futures_util::TryFutureExt as _;
93
94 use super::*;
95 use crate::{
96 dev::Service,
97 http::{Method, StatusCode},
98 test::{init_service, TestRequest},
99 web, App, HttpMessage, HttpResponse,
100 };
101
102 #[actix_rt::test]
103 async fn req_data_extractor() {
104 let srv = init_service(
105 App::new()
106 .wrap_fn(|req, srv| {
107 if req.method() == Method::POST {
108 req.extensions_mut().insert(42u32);
109 }
110
111 srv.call(req)
112 })
113 .service(web::resource("/test").to(
114 |req: HttpRequest, data: Option<ReqData<u32>>| {
115 if req.method() != Method::POST {
116 assert!(data.is_none());
117 }
118
119 if let Some(data) = data {
120 assert_eq!(*data, 42);
121 assert_eq!(
122 Some(data.into_inner()),
123 req.extensions().get::<u32>().copied()
124 );
125 }
126
127 HttpResponse::Ok()
128 },
129 )),
130 )
131 .await;
132
133 let req = TestRequest::get().uri("/test").to_request();
134 let resp = srv.call(req).await.unwrap();
135 assert_eq!(resp.status(), StatusCode::OK);
136
137 let req = TestRequest::post().uri("/test").to_request();
138 let resp = srv.call(req).await.unwrap();
139 assert_eq!(resp.status(), StatusCode::OK);
140 }
141
142 #[actix_rt::test]
143 async fn req_data_internal_mutability() {
144 let srv = init_service(
145 App::new()
146 .wrap_fn(|req, srv| {
147 let data_before = Rc::new(RefCell::new(42u32));
148 req.extensions_mut().insert(data_before);
149
150 srv.call(req).map_ok(|res| {
151 {
152 let ext = res.request().extensions();
153 let data_after = ext.get::<Rc<RefCell<u32>>>().unwrap();
154 assert_eq!(*data_after.borrow(), 53u32);
155 }
156
157 res
158 })
159 })
160 .default_service(web::to(|data: ReqData<Rc<RefCell<u32>>>| {
161 assert_eq!(*data.borrow(), 42);
162 *data.borrow_mut() += 11;
163 assert_eq!(*data.borrow(), 53);
164
165 HttpResponse::Ok()
166 })),
167 )
168 .await;
169
170 let req = TestRequest::get().uri("/test").to_request();
171 let resp = srv.call(req).await.unwrap();
172 assert_eq!(resp.status(), StatusCode::OK);
173 }
174}