Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to improve the documentation for App::data() #1736

Closed
wants to merge 9 commits into from
97 changes: 83 additions & 14 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,38 @@ where
InitError = (),
>,
{
/// Set application data. Application data could be accessed
/// by using `Data<T>` extractor where `T` is data type.
/// Adds an arbitrary application level data item.
///
/// **Note**: http server accepts an application factory rather than
/// an application instance. Http server constructs an application
/// instance for each thread, thus application data must be constructed
/// multiple times. If you want to share data between different
/// threads, a shared object should be used, e.g. `Arc`. Internally `Data` type
/// uses `Arc` so data could be created outside of app factory and clones could
/// be stored via `App::app_data()` method.
/// Application data can be accessed by using a `Data<T>` extractor where `T` is the data type.
///
/// The state is managed on a per-type basis and as such there can be
/// at most one value for any given type.
/// This means that only the first invocation of this function per type will have an effect,
/// all later ones will be ignored.
Comment on lines +80 to +81
Copy link
Member

@robjtede robjtede Jan 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this true, or is it the last invocation is used?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did look at the code back then and I thought this is what it does but I can take another look.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to be honest: I don't understand (anymore) how this is used. If one of you has any insights I'll happily add it otherwise I'd just remove that sentence.

It would be good to document the behavior though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know what you're right, and that's surprising behavior actually because .app_data works differently; a bug, even.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity (because I couldn't trace it anymore but I know I did in the past): Can you point me at the location where this happens?

///
/// Internally the data will be wrapped in an `Arc`.
/// If your data is already wrapped in an `Arc`
/// you can instead store it directly using the `App::app_data()` function.
robjtede marked this conversation as resolved.
Show resolved Hide resolved
/// Note: There is a `From<Arc<T>>` implementation for `Data<T>` so anything wrapped in an `Arc`
/// can be converted to a `Data` like this:
///
/// ```rust
/// use std::sync::Arc;
/// use actix_web::web::Data;
///
/// let arc = Arc::new(String::from("test-123"));
/// let data_from_arc = Data::from(arc);
/// ```
///
/// **Note**: `HttpServer` accepts an application factory (closure) rather than
/// an application instance (`App`).
/// `HttpServer` constructs an application instance for each thread
/// by calling this factory closure thus application data must be constructed multiple times.
/// If you want to share data between different threads,
/// a shared object should be used, e.g. `Arc`.
///
/// If route data is not set for a handler, using `Data<T>` extractor would cause an *Internal
/// Server Error* response.
///
/// ```rust
/// use std::cell::Cell;
Expand Down Expand Up @@ -137,13 +159,60 @@ where
self
}

/// Set application level arbitrary data item.
/// Adds an arbirtraty application level data item.
///
/// This data is available to all routes and can be added during the application.
/// There are two ways to retrieve this data:
///
/// 1. At runtime via the `HttpRequest::app_data()` method
/// 2. If data of type `T` is stored wrapped in a `Data<T>` object
/// it can also be retrieved using a `Data<T>` extractor
///
/// Application data stored with `App::app_data()` method is available
/// via `HttpRequest::app_data()` method at runtime.
/// The state is managed on a per-type basis and as such there can be
/// at most one value for any given type.
/// Later invocations overwrite earlier ones.
/// This means that only the last invocation of this function per type will have an effect.
///
/// **Note**: `HttpServer` accepts an application factory (closure) rather than
/// an application instance (`App`).
/// `HttpServer` constructs an application instance for each thread
/// by calling this factory closure thus application data must be constructed multiple times.
/// If you want to share data between different threads,
/// a shared object should be used, e.g. `Arc`.
///
/// ```rust
/// use std::sync::Mutex;
/// use actix_web::{web, App, HttpResponse, HttpRequest, Responder};
///
/// This method could be used for storing `Data<T>` as well, in that case
/// data could be accessed by using `Data<T>` extractor.
/// struct MyData {
/// counter: usize,
/// }
///
/// /// Use the `Data<T>` extractor to access data in a handler.
/// async fn index(data: web::Data<Mutex<MyData>>) -> impl Responder {
/// let mut data = data.lock().unwrap();
/// data.counter += 1;
/// HttpResponse::Ok().body(format!("{}", data.counter))
/// }
///
/// async fn hello(req: HttpRequest) -> impl Responder {
/// let data = req.app_data::<web::Data<Mutex<MyData>>>();
/// let mut data = data.unwrap().lock().unwrap();
/// data.counter += 1;
/// HttpResponse::Ok().body(format!("{}", data.counter))
///}
///
/// fn main() {
/// let data = web::Data::new(Mutex::new(MyData{ counter: 0 }));
///
/// let app = App::new()
/// // Store `MyData` in application storage.
/// .app_data(data)
/// .service(
/// web::resource("/index.html").route(
/// web::get().to(index)));
/// }
/// ```
pub fn app_data<U: 'static>(mut self, ext: U) -> Self {
self.extensions.insert(ext);
self
Expand Down
44 changes: 3 additions & 41 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,11 @@ pub(crate) trait DataFactory {
pub(crate) type FnDataFactory =
Box<dyn Fn() -> LocalBoxFuture<'static, Result<Box<dyn DataFactory>, ()>>>;

/// Application data.
/// Wrapper for a piece of data.
///
/// Application level data is a piece of arbitrary data attached to the app, scope, or resource.
/// Application data is available to all routes and can be added during the application
/// configuration process via `App::data()`.
/// Internally it wraps the data in an `Arc`.
///
/// Application data can be accessed by using `Data<T>` extractor where `T` is data type.
///
/// **Note**: http server accepts an application factory rather than an application instance. HTTP
/// server constructs an application instance for each thread, thus application data must be
/// 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`
/// or `Sync`. Internally `Data` uses `Arc`.
///
/// If route data is not set for a handler, using `Data<T>` extractor would cause *Internal
/// Server Error* response.
///
/// ```rust
/// use std::sync::Mutex;
/// use actix_web::{web, App, HttpResponse, Responder};
///
/// struct MyData {
/// counter: usize,
/// }
///
/// /// Use the `Data<T>` extractor to access data in a handler.
/// async fn index(data: web::Data<Mutex<MyData>>) -> impl Responder {
/// let mut data = data.lock().unwrap();
/// data.counter += 1;
/// HttpResponse::Ok()
/// }
///
/// fn main() {
/// let data = web::Data::new(Mutex::new(MyData{ counter: 0 }));
///
/// let app = App::new()
/// // Store `MyData` in application storage.
/// .app_data(data.clone())
/// .service(
/// web::resource("/index.html").route(
/// web::get().to(index)));
/// }
/// ```
/// See [`App::data()`] and [`App::app_data()`] for when and how to use this.
#[derive(Debug)]
pub struct Data<T: ?Sized>(Arc<T>);

Expand Down