1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

improve application data docs

This commit is contained in:
Rob Ede 2021-12-30 07:11:35 +00:00
parent 6df4974234
commit 231a24ef8d
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
5 changed files with 58 additions and 21 deletions

View File

@ -109,6 +109,7 @@ impl<T> App<T> {
/// .route("/", web::get().to(handler)) /// .route("/", web::get().to(handler))
/// }) /// })
/// ``` /// ```
#[doc(alias = "manage")]
pub fn app_data<U: 'static>(mut self, ext: U) -> Self { pub fn app_data<U: 'static>(mut self, ext: U) -> Self {
self.extensions.insert(ext); self.extensions.insert(ext);
self self

View File

@ -19,23 +19,32 @@ pub(crate) trait DataFactory {
pub(crate) type FnDataFactory = pub(crate) type FnDataFactory =
Box<dyn Fn() -> LocalBoxFuture<'static, Result<Box<dyn DataFactory>, ()>>>; Box<dyn Fn() -> LocalBoxFuture<'static, Result<Box<dyn DataFactory>, ()>>>;
/// Application data. /// Application data wrapper and extractor.
/// ///
/// Application level data is a piece of arbitrary data attached to the app, scope, or resource. /// # Setting Data
/// Application data is available to all routes and can be added during the application /// Data is set using the `app_data` methods on `App`, `Scope`, and `Resource`. If data is wrapped
/// configuration process via `App::data()`. /// in this `Data` type for those calls, it can be used as an extractor.
/// ///
/// Application data can be accessed by using `Data<T>` extractor where `T` is data type. /// Note that `Data` should be constructed _outside_ the `HttpServer::new` closure if shared,
/// potentially mutable state is desired. `Data` is cheap to clone; internally, it uses an `Arc`.
/// ///
/// **Note**: HTTP server accepts an application factory rather than an application instance. HTTP /// See also [`App::app_data`](crate::App::app_data), [`Scope::app_data`](crate::Scope::app_data),
/// server constructs an application instance for each thread, thus application data must be /// and [`Resource::app_data`](crate::Resource::app_data).
/// constructed multiple times. If you want to share data between different threads, a shareable ///
/// object should be used, e.g. `Send + Sync`. Application data does not need to be `Send` /// # Extracting `Data`
/// or `Sync`. Internally `Data` contains an `Arc`. /// Since the Actix Web router layers application data, the returned object will reference the
/// "closest" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`
/// also stores a `u32`, and the delegated request handler falls within that `Scope`, then
/// extracting a `web::<Data<u32>>` for that handler will return the `Scope`'s instance.
/// However, using the same router set up and a request that does not get captured by the `Scope`,
/// `web::<Data<u32>>` would return the `App`'s instance.
/// ///
/// If route data is not set for a handler, using `Data<T>` extractor would cause a `500 Internal /// If route data is not set for a handler, using `Data<T>` extractor would cause a `500 Internal
/// Server Error` response. /// Server Error` response.
/// ///
/// See also [`HttpRequest::app_data`]
/// and [`ServiceRequest::app_data`](crate::dev::ServiceRequest::app_data).
///
/// # Unsized Data /// # Unsized Data
/// For types that are unsized, most commonly `dyn T`, `Data` can wrap these types by first /// For types that are unsized, most commonly `dyn T`, `Data` can wrap these types by first
/// constructing an `Arc<dyn T>` and using the `From` implementation to convert it. /// constructing an `Arc<dyn T>` and using the `From` implementation to convert it.
@ -79,6 +88,7 @@ pub(crate) type FnDataFactory =
/// .route("/index.html", web::get().to(index)) /// .route("/index.html", web::get().to(index))
/// .route("/index-alt.html", web::get().to(index_alt)); /// .route("/index-alt.html", web::get().to(index_alt));
/// ``` /// ```
#[doc(alias = "state")]
#[derive(Debug)] #[derive(Debug)]
pub struct Data<T: ?Sized>(Arc<T>); pub struct Data<T: ?Sized>(Arc<T>);
@ -90,12 +100,12 @@ impl<T> Data<T> {
} }
impl<T: ?Sized> Data<T> { impl<T: ?Sized> Data<T> {
/// Get reference to inner app data. /// Returns reference to inner `T`.
pub fn get_ref(&self) -> &T { pub fn get_ref(&self) -> &T {
self.0.as_ref() self.0.as_ref()
} }
/// Convert to the internal Arc<T> /// Unwraps to the internal `Arc<T>`
pub fn into_inner(self) -> Arc<T> { pub fn into_inner(self) -> Arc<T> {
self.0 self.0
} }
@ -143,13 +153,17 @@ impl<T: ?Sized + 'static> FromRequest for Data<T> {
ok(st.clone()) ok(st.clone())
} else { } else {
log::debug!( log::debug!(
"Failed to construct App-level Data extractor. \ "Failed to construct Data extractor type: `{}`. For the Data extractor to work \
Request path: {:?} (type: {})", correctly, wrap the data with `Data::new()` and pass it to `App::app_data()`. \
req.path(), Ensure that types align in both the set and retrieve calls. \
Request path: {}",
type_name::<T>(), type_name::<T>(),
req.path()
); );
err(ErrorInternalServerError( err(ErrorInternalServerError(
"App data is not configured, to configure construct it with web::Data::new() and pass it to App::app_data()", "Requested application data is not configured. \
View/enable debug logs for more details.",
)) ))
} }
} }

View File

@ -266,14 +266,34 @@ impl HttpRequest {
self.app_state().config() self.app_state().config()
} }
/// Get an application data object stored with `App::data` or `App::app_data` /// Retrieves a piece of application state.
/// methods during application configuration.
/// ///
/// If `App::data` was used to store object, use `Data<T>`: /// Extracts any object stored with [`App::app_data()`](crate::App::app_data) (or the
/// counterpart methods on [`Scope`](crate::Scope::app_data) and
/// [`Resource`](crate::Resource::app_data)) during application configuration.
/// ///
/// ```ignore /// Since the Actix Web router layers application data, the returned object will reference the
/// let opt_t = req.app_data::<Data<T>>(); /// "closest" instance of the type. For example, if an `App` stores a `u32`, a nested `Scope`
/// also stores a `u32`, and the delegated request handler falls within that `Scope`, then
/// calling `.app_data::<u32>()` on an `HttpRequest` within that handler will return the
/// `Scope`'s instance. However, using the same router set up and a request that does not get
/// captured by the `Scope`, `.app_data::<u32>()` would return the `App`'s instance.
///
/// If the state was stored using the [`Data`] wrapper, then it must also be retrieved using
/// this same type.
///
/// See also the [`Data`] extractor.
///
/// # Examples
/// ```no_run
/// # use actix_web::{web, test::TestRequest};
/// # let req = TestRequest::default().to_http_request();
/// # type T = u32;
/// let opt_t: Option<&T> = req.app_data::<web::Data<T>>();
/// ``` /// ```
///
/// [`Data`]: crate::web::Data
#[doc(alias = "state")]
pub fn app_data<T: 'static>(&self) -> Option<&T> { pub fn app_data<T: 'static>(&self) -> Option<&T> {
for container in self.inner.app_data.iter().rev() { for container in self.inner.app_data.iter().rev() {
if let Some(data) = container.get::<T>() { if let Some(data) = container.get::<T>() {

View File

@ -195,6 +195,7 @@ where
/// .route(web::get().to(handler)) /// .route(web::get().to(handler))
/// ); /// );
/// ``` /// ```
#[doc(alias = "manage")]
pub fn app_data<U: 'static>(mut self, data: U) -> Self { pub fn app_data<U: 'static>(mut self, data: U) -> Self {
self.app_data self.app_data
.get_or_insert_with(Extensions::new) .get_or_insert_with(Extensions::new)

View File

@ -154,6 +154,7 @@ where
/// .route("/", web::get().to(handler)) /// .route("/", web::get().to(handler))
/// ); /// );
/// ``` /// ```
#[doc(alias = "manage")]
pub fn app_data<U: 'static>(mut self, data: U) -> Self { pub fn app_data<U: 'static>(mut self, data: U) -> Self {
self.app_data self.app_data
.get_or_insert_with(Extensions::new) .get_or_insert_with(Extensions::new)