actix_web/
thin_data.rs

1use std::any::type_name;
2
3use actix_utils::future::{ready, Ready};
4
5use crate::{dev::Payload, error, FromRequest, HttpRequest};
6
7/// Application data wrapper and extractor for cheaply-cloned types.
8///
9/// Similar to the [`Data`] wrapper but for `Clone`/`Copy` types that are already an `Arc` internally,
10/// share state using some other means when cloned, or is otherwise static data that is very cheap
11/// to clone.
12///
13/// Unlike `Data`, this wrapper clones `T` during extraction. Therefore, it is the user's
14/// responsibility to ensure that clones of `T` do actually share the same state, otherwise state
15/// may be unexpectedly different across multiple requests.
16///
17/// Note that if your type is literally an `Arc<T>` then it's recommended to use the
18/// [`Data::from(arc)`][data_from_arc] conversion instead.
19///
20/// # Examples
21///
22/// ```
23/// use actix_web::{
24///     web::{self, ThinData},
25///     App, HttpResponse, Responder,
26/// };
27///
28/// // Use the `ThinData<T>` extractor to access a database connection pool.
29/// async fn index(ThinData(db_pool): ThinData<DbPool>) -> impl Responder {
30///     // database action ...
31///
32///     HttpResponse::Ok()
33/// }
34///
35/// # type DbPool = ();
36/// let db_pool = DbPool::default();
37///
38/// App::new()
39///     .app_data(ThinData(db_pool.clone()))
40///     .service(web::resource("/").get(index))
41/// # ;
42/// ```
43///
44/// [`Data`]: crate::web::Data
45/// [data_from_arc]: crate::web::Data#impl-From<Arc<T>>-for-Data<T>
46#[derive(Debug, Clone)]
47pub struct ThinData<T>(pub T);
48
49impl_more::impl_as_ref!(ThinData<T> => T);
50impl_more::impl_as_mut!(ThinData<T> => T);
51impl_more::impl_deref_and_mut!(<T> in ThinData<T> => T);
52
53impl<T: Clone + 'static> FromRequest for ThinData<T> {
54    type Error = crate::Error;
55    type Future = Ready<Result<Self, Self::Error>>;
56
57    #[inline]
58    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
59        ready(req.app_data::<Self>().cloned().ok_or_else(|| {
60            log::debug!(
61                "Failed to extract `ThinData<{}>` for `{}` handler. For the ThinData extractor to work \
62                correctly, wrap the data with `ThinData()` and pass it to `App::app_data()`. \
63                Ensure that types align in both the set and retrieve calls.",
64                type_name::<T>(),
65                req.match_name().unwrap_or(req.path())
66            );
67
68            error::ErrorInternalServerError(
69                "Requested application data is not configured correctly. \
70                View/enable debug logs for more details.",
71            )
72        }))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use std::sync::{Arc, Mutex};
79
80    use super::*;
81    use crate::{
82        http::StatusCode,
83        test::{call_service, init_service, TestRequest},
84        web, App, HttpResponse,
85    };
86
87    type TestT = Arc<Mutex<u32>>;
88
89    #[actix_rt::test]
90    async fn thin_data() {
91        let test_data = TestT::default();
92
93        let app = init_service(App::new().app_data(ThinData(test_data.clone())).service(
94            web::resource("/").to(|td: ThinData<TestT>| {
95                *td.lock().unwrap() += 1;
96                HttpResponse::Ok()
97            }),
98        ))
99        .await;
100
101        for _ in 0..3 {
102            let req = TestRequest::default().to_request();
103            let resp = call_service(&app, req).await;
104            assert_eq!(resp.status(), StatusCode::OK);
105        }
106
107        assert_eq!(*test_data.lock().unwrap(), 3);
108    }
109
110    #[actix_rt::test]
111    async fn thin_data_missing() {
112        let app = init_service(
113            App::new().service(web::resource("/").to(|_: ThinData<u32>| HttpResponse::Ok())),
114        )
115        .await;
116
117        let req = TestRequest::default().to_request();
118        let resp = call_service(&app, req).await;
119        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
120    }
121}