actix_web/
request_data.rs1use std::{any::type_name, ops::Deref};
2
3#[expect(deprecated, reason = "preserve the public ready future type")]
4use actix_utils::future::{ready, Ready};
5
6use crate::{
7 dev::Payload, error::ErrorInternalServerError, Error, FromRequest, HttpMessage as _,
8 HttpRequest,
9};
10
11#[derive(Debug, Clone)]
51pub struct ReqData<T: Clone + 'static>(T);
52
53impl<T: Clone + 'static> ReqData<T> {
54 pub fn into_inner(self) -> T {
56 self.0
57 }
58}
59
60impl<T: Clone + 'static> Deref for ReqData<T> {
61 type Target = T;
62
63 fn deref(&self) -> &T {
64 &self.0
65 }
66}
67
68#[expect(deprecated, reason = "preserve the public ready future type")]
69impl<T: Clone + 'static> FromRequest for ReqData<T> {
70 type Error = Error;
71 type Future = Ready<Result<Self, Error>>;
72
73 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
74 if let Some(st) = req.extensions().get::<T>() {
75 ready(Ok(ReqData(st.clone())))
76 } else {
77 log::debug!(
78 "Failed to construct App-level ReqData extractor. \
79 Request path: {:?} (type: {})",
80 req.path(),
81 type_name::<T>(),
82 );
83 ready(Err(ErrorInternalServerError(
84 "Missing expected request extension data",
85 )))
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use std::{cell::RefCell, rc::Rc};
93
94 use futures_util::TryFutureExt as _;
95
96 use super::*;
97 use crate::{
98 dev::Service,
99 http::{Method, StatusCode},
100 test::{init_service, TestRequest},
101 web, App, HttpMessage, HttpResponse,
102 };
103
104 #[actix_rt::test]
105 async fn req_data_extractor() {
106 let srv = init_service(
107 App::new()
108 .wrap_fn(|req, srv| {
109 if req.method() == Method::POST {
110 req.extensions_mut().insert(42u32);
111 }
112
113 srv.call(req)
114 })
115 .service(web::resource("/test").to(
116 |req: HttpRequest, data: Option<ReqData<u32>>| {
117 if req.method() != Method::POST {
118 assert!(data.is_none());
119 }
120
121 if let Some(data) = data {
122 assert_eq!(*data, 42);
123 assert_eq!(
124 Some(data.into_inner()),
125 req.extensions().get::<u32>().copied()
126 );
127 }
128
129 HttpResponse::Ok()
130 },
131 )),
132 )
133 .await;
134
135 let req = TestRequest::get().uri("/test").to_request();
136 let resp = srv.call(req).await.unwrap();
137 assert_eq!(resp.status(), StatusCode::OK);
138
139 let req = TestRequest::post().uri("/test").to_request();
140 let resp = srv.call(req).await.unwrap();
141 assert_eq!(resp.status(), StatusCode::OK);
142 }
143
144 #[actix_rt::test]
145 async fn req_data_internal_mutability() {
146 let srv = init_service(
147 App::new()
148 .wrap_fn(|req, srv| {
149 let data_before = Rc::new(RefCell::new(42u32));
150 req.extensions_mut().insert(data_before);
151
152 srv.call(req).map_ok(|res| {
153 {
154 let ext = res.request().extensions();
155 let data_after = ext.get::<Rc<RefCell<u32>>>().unwrap();
156 assert_eq!(*data_after.borrow(), 53u32);
157 }
158
159 res
160 })
161 })
162 .default_service(web::to(|data: ReqData<Rc<RefCell<u32>>>| {
163 assert_eq!(*data.borrow(), 42);
164 *data.borrow_mut() += 11;
165 assert_eq!(*data.borrow(), 53);
166
167 HttpResponse::Ok()
168 })),
169 )
170 .await;
171
172 let req = TestRequest::get().uri("/test").to_request();
173 let resp = srv.call(req).await.unwrap();
174 assert_eq!(resp.status(), StatusCode::OK);
175 }
176}