mirror of
https://github.com/actix/actix-web.git
synced 2025-04-12 21:04:05 +00:00
Add DataRaw type.
This commit is contained in:
parent
ace98e3a1e
commit
29b6c77067
8 changed files with 302 additions and 43 deletions
11
src/app.rs
11
src/app.rs
|
@ -12,7 +12,7 @@ use futures::{Future, IntoFuture};
|
|||
|
||||
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
|
||||
use crate::config::{AppConfig, AppConfigInner, ServiceConfig};
|
||||
use crate::data::{Data, DataFactory};
|
||||
use crate::data::{AppData, Data, DataFactory, DataRaw};
|
||||
use crate::dev::ResourceDef;
|
||||
use crate::error::Error;
|
||||
use crate::resource::Resource;
|
||||
|
@ -104,6 +104,11 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
pub fn data_raw<U: Send + Sync + Clone + 'static>(mut self, data: U) -> Self {
|
||||
self.data.push(Box::new(DataRaw::new(data)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set application data factory. This function is
|
||||
/// similar to `.data()` but it accepts data factory. Data object get
|
||||
/// constructed asynchronously during application initialization.
|
||||
|
@ -130,8 +135,8 @@ where
|
|||
}
|
||||
|
||||
/// Set application data. Application data could be accessed
|
||||
/// by using `Data<T>` extractor where `T` is data type.
|
||||
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
|
||||
/// by using `Data<T>`or `DataRaw<T>` extractor where `T` is data type.
|
||||
pub fn register_data<U: AppData + Clone + 'static>(mut self, data: U) -> Self {
|
||||
self.data.push(Box::new(data));
|
||||
self
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use actix_http::Extensions;
|
|||
use actix_router::ResourceDef;
|
||||
use actix_service::{boxed, IntoNewService, NewService};
|
||||
|
||||
use crate::data::{Data, DataFactory};
|
||||
use crate::data::{AppData, Data, DataFactory, DataRaw};
|
||||
use crate::error::Error;
|
||||
use crate::guard::Guard;
|
||||
use crate::resource::Resource;
|
||||
|
@ -197,6 +197,16 @@ impl ServiceConfig {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set raw application data. Raw application data could be accessed
|
||||
/// by using `DataRaw<T>` extractor where `T` is data type.
|
||||
/// T must be `Send + Sync + Clone` in order to be register as `DataRaw`
|
||||
///
|
||||
/// This is same as `App::data_raw()` method.
|
||||
pub fn data_raw<U: Send + Sync + Clone + 'static>(&mut self, data: U) -> &mut Self {
|
||||
self.data.push(Box::new(DataRaw::new(data)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure route for a specific path.
|
||||
///
|
||||
/// This is same as `App::route()` method.
|
||||
|
@ -241,6 +251,8 @@ impl ServiceConfig {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use actix_service::Service;
|
||||
use bytes::Bytes;
|
||||
|
||||
|
@ -264,6 +276,21 @@ mod tests {
|
|||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_raw() {
|
||||
let cfg = |cfg: &mut ServiceConfig| {
|
||||
cfg.data_raw(Arc::new(Mutex::new(10usize)));
|
||||
};
|
||||
|
||||
let mut srv =
|
||||
init_service(App::new().configure(cfg).service(
|
||||
web::resource("/").to(|_: web::DataRaw<Arc<Mutex<usize>>>| HttpResponse::Ok()),
|
||||
));
|
||||
let req = TestRequest::default().to_request();
|
||||
let resp = block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_data_factory() {
|
||||
// let cfg = |cfg: &mut ServiceConfig| {
|
||||
|
|
191
src/data.rs
191
src/data.rs
|
@ -13,6 +13,21 @@ pub(crate) trait DataFactory {
|
|||
fn create(&self, extensions: &mut Extensions) -> bool;
|
||||
}
|
||||
|
||||
/// AppData is a trait generic over `Data<T>` and `DataRaw<T>`.
|
||||
/// A type impl this trait can use `FromRequest` and `DataFactory` to extract and push data to extension
|
||||
pub trait AppData {
|
||||
type Data;
|
||||
type Inner;
|
||||
/// Create new `Data` instance.
|
||||
fn new(state: Self::Data) -> Self;
|
||||
|
||||
/// Get reference to inner app data.
|
||||
fn get_ref(&self) -> &Self::Data;
|
||||
|
||||
/// Convert to the internal Data.
|
||||
fn into_inner(self) -> Self::Inner;
|
||||
}
|
||||
|
||||
/// Application data.
|
||||
///
|
||||
/// Application data is an arbitrary data attached to the app.
|
||||
|
@ -37,7 +52,7 @@ pub(crate) trait DataFactory {
|
|||
///
|
||||
/// ```rust
|
||||
/// use std::sync::Mutex;
|
||||
/// use actix_web::{web, App};
|
||||
/// use actix_web::{web::{self, AppData}, App};
|
||||
///
|
||||
/// struct MyData {
|
||||
/// counter: usize,
|
||||
|
@ -63,27 +78,6 @@ pub(crate) trait DataFactory {
|
|||
#[derive(Debug)]
|
||||
pub struct Data<T>(Arc<T>);
|
||||
|
||||
impl<T> Data<T> {
|
||||
/// Create new `Data` instance.
|
||||
///
|
||||
/// Internally `Data` type uses `Arc`. if your data implements
|
||||
/// `Send` + `Sync` traits you can use `web::Data::new()` and
|
||||
/// avoid double `Arc`.
|
||||
pub fn new(state: T) -> Data<T> {
|
||||
Data(Arc::new(state))
|
||||
}
|
||||
|
||||
/// Get reference to inner app data.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.0.as_ref()
|
||||
}
|
||||
|
||||
/// Convert to the internal Arc<T>
|
||||
pub fn into_inner(self) -> Arc<T> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Data<T> {
|
||||
type Target = T;
|
||||
|
||||
|
@ -98,7 +92,99 @@ impl<T> Clone for Data<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromRequest for Data<T> {
|
||||
impl<T> AppData for Data<T> {
|
||||
type Data = T;
|
||||
type Inner = Arc<T>;
|
||||
fn new(state: Self::Data) -> Self {
|
||||
Data(Arc::new(state))
|
||||
}
|
||||
|
||||
fn get_ref(&self) -> &Self::Data {
|
||||
self.0.as_ref()
|
||||
}
|
||||
|
||||
fn into_inner(self) -> Self::Inner {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw Application data.
|
||||
///
|
||||
/// Raw Application data shares the same principle of Application data.
|
||||
/// The difference is Raw Application data explicitly require the data type to be `Send + Sync + Clone`.
|
||||
///
|
||||
/// This is useful when you introduce a foreign type(from other crates for example) that is already thread safe.
|
||||
/// By using Raw Application data you can avoid the additional layer of `Arc` provided by `web::Data`
|
||||
/// ```rust
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use actix_web::{web::{self, AppData}, App};
|
||||
///
|
||||
/// struct ForeignType {
|
||||
/// inner: Arc<Mutex<usize>>
|
||||
/// }
|
||||
///
|
||||
/// impl Clone for ForeignType {
|
||||
/// fn clone(&self) -> Self {
|
||||
/// ForeignType {
|
||||
/// inner: self.inner.clone()
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Use `DataRaw<T>` extractor to access data in handler.
|
||||
/// fn index(data: web::DataRaw<ForeignType>) {
|
||||
/// let mut data = data.inner.lock().unwrap();
|
||||
/// *data += 1;
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let data = ForeignType {
|
||||
/// inner: Arc::new(Mutex::new(1usize))
|
||||
/// };
|
||||
///
|
||||
/// let app = App::new()
|
||||
/// // Store `ForeignTypeType` in application storage.
|
||||
/// .data_raw(data.clone())
|
||||
/// .service(
|
||||
/// web::resource("/index.html").route(
|
||||
/// web::get().to(index)));
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct DataRaw<T: Send + Sync + Clone>(T);
|
||||
|
||||
impl<T: Send + Sync + Clone> Clone for DataRaw<T> {
|
||||
fn clone(&self) -> DataRaw<T> {
|
||||
DataRaw(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone> Deref for DataRaw<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AppData for DataRaw<T>
|
||||
where T: Send + Sync + Clone {
|
||||
type Data = T;
|
||||
type Inner = T;
|
||||
fn new(state: Self::Data) -> Self {
|
||||
DataRaw(state)
|
||||
}
|
||||
|
||||
fn get_ref(&self) -> &Self::Data {
|
||||
&*self
|
||||
}
|
||||
|
||||
fn into_inner(self) -> Self::Inner {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AppData + Clone + 'static> FromRequest for T {
|
||||
type Config = ();
|
||||
type Error = Error;
|
||||
type Future = Result<Self, Error>;
|
||||
|
@ -120,10 +206,10 @@ impl<T: 'static> FromRequest for Data<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> DataFactory for Data<T> {
|
||||
impl<T: AppData + Clone + 'static> DataFactory for T {
|
||||
fn create(&self, extensions: &mut Extensions) -> bool {
|
||||
if !extensions.contains::<Data<T>>() {
|
||||
extensions.insert(Data(self.0.clone()));
|
||||
if !extensions.contains::<T>() {
|
||||
extensions.insert(self.clone());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -133,6 +219,9 @@ impl<T: 'static> DataFactory for Data<T> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::{AtomicUsize, AtomicU32};
|
||||
|
||||
use actix_service::Service;
|
||||
|
||||
use super::*;
|
||||
|
@ -223,4 +312,54 @@ mod tests {
|
|||
let resp = block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_raw_extractor() {
|
||||
let mut srv =
|
||||
init_service(App::new().data_raw(Arc::new(Mutex::new(1usize))).service(
|
||||
web::resource("/").to(|_: web::DataRaw<Arc<Mutex<usize>>>| HttpResponse::Ok()),
|
||||
));
|
||||
|
||||
let req = TestRequest::default().to_request();
|
||||
let resp = block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
||||
let mut srv =
|
||||
init_service(App::new().data_raw(Arc::new(AtomicUsize::new(1))).service(
|
||||
web::resource("/").to(|_: web::DataRaw<Arc<AtomicUsize>>| HttpResponse::Ok()),
|
||||
));
|
||||
|
||||
let req = TestRequest::default().to_request();
|
||||
let resp = block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
||||
let mut srv =
|
||||
init_service(App::new().data_raw(Arc::new(AtomicUsize::new(1))).service(
|
||||
web::resource("/").to(|_: web::DataRaw<Arc<AtomicU32>>| HttpResponse::Ok()),
|
||||
));
|
||||
let req = TestRequest::default().to_request();
|
||||
let resp = block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_data_raw_extractor() {
|
||||
let mut srv =
|
||||
init_service(App::new().register_data(DataRaw::new(Arc::new(AtomicUsize::new(1)))).service(
|
||||
web::resource("/").to(|_: web::DataRaw<Arc<AtomicUsize>>| HttpResponse::Ok()),
|
||||
));
|
||||
|
||||
let req = TestRequest::default().to_request();
|
||||
let resp = block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
||||
let mut srv =
|
||||
init_service(App::new().register_data(DataRaw::new(Arc::new(AtomicUsize::new(1)))).service(
|
||||
web::resource("/").to(|_: web::DataRaw<Arc<AtomicU32>>| HttpResponse::Ok()),
|
||||
));
|
||||
let req = TestRequest::default().to_request();
|
||||
let resp = block_on(srv.call(req)).unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -218,8 +218,8 @@ impl HttpRequest {
|
|||
|
||||
/// Get an application data stored with `App::data()` method during
|
||||
/// application configuration.
|
||||
pub fn get_app_data<T: 'static>(&self) -> Option<Data<T>> {
|
||||
if let Some(st) = self.0.app_data.get::<Data<T>>() {
|
||||
pub fn get_app_data<T: Clone + 'static>(&self) -> Option<T> {
|
||||
if let Some(st) = self.0.app_data.get::<T>() {
|
||||
Some(st.clone())
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -10,7 +10,7 @@ use actix_service::{
|
|||
use futures::future::{ok, Either, FutureResult};
|
||||
use futures::{Async, Future, IntoFuture, Poll};
|
||||
|
||||
use crate::data::Data;
|
||||
use crate::data::{AppData, Data, DataRaw};
|
||||
use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef};
|
||||
use crate::extract::FromRequest;
|
||||
use crate::guard::Guard;
|
||||
|
@ -193,13 +193,19 @@ where
|
|||
self.register_data(Data::new(data))
|
||||
}
|
||||
|
||||
/// The same usage as `Resource::data` method.
|
||||
/// The difference is the `U` must be `Send + Sync + Clone` in order to register as `DataRaw`
|
||||
pub fn data_raw<U: Send + Sync + Clone + 'static>(self, data: U) -> Self {
|
||||
self.register_data(DataRaw::new(data))
|
||||
}
|
||||
|
||||
/// Set or override application data.
|
||||
///
|
||||
/// This method has the same effect as [`Resource::data`](#method.data),
|
||||
/// except that instead of taking a value of some type `T`, it expects a
|
||||
/// value of type `Data<T>`. Use a `Data<T>` extractor to retrieve its
|
||||
/// value.
|
||||
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
|
||||
/// value of type `Data<T>` or `DataRaw<T>`. Use a `Data<T>` or `DataRaw<T>`
|
||||
/// extractor to retrieve its value.
|
||||
pub fn register_data<U: AppData + 'static>(mut self, data: U) -> Self {
|
||||
if self.data.is_none() {
|
||||
self.data = Some(Extensions::new());
|
||||
}
|
||||
|
@ -608,11 +614,13 @@ impl NewService for ResourceEndpoint {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use actix_service::Service;
|
||||
use futures::{Future, IntoFuture};
|
||||
use tokio_timer::sleep;
|
||||
|
||||
use crate::data::AppData;
|
||||
use crate::http::{header, HeaderValue, Method, StatusCode};
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
use crate::test::{call_service, init_service, TestRequest};
|
||||
|
@ -803,4 +811,34 @@ mod tests {
|
|||
let resp = call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_raw() {
|
||||
let mut srv = init_service(
|
||||
App::new()
|
||||
.data_raw(Arc::new(Mutex::new(1.0f64)))
|
||||
.data_raw(Arc::new(Mutex::new(1usize)))
|
||||
.register_data(web::DataRaw::new(Arc::new(Mutex::new('-'))))
|
||||
.service(
|
||||
web::resource("/test")
|
||||
.data_raw(Arc::new(Mutex::new(10usize)))
|
||||
.register_data(web::DataRaw::new(Arc::new(Mutex::new('*'))))
|
||||
.guard(guard::Get())
|
||||
.to(
|
||||
|data1: web::DataRaw<Arc<Mutex<usize>>>,
|
||||
data2: web::DataRaw<Arc<Mutex<char>>>,
|
||||
data3: web::DataRaw<Arc<Mutex<f64>>>| {
|
||||
assert_eq!(*(data1.lock().unwrap()), 10);
|
||||
assert_eq!(*(data2.lock().unwrap()), '*');
|
||||
assert_eq!(*(data3.lock().unwrap()), 1.0);
|
||||
HttpResponse::Ok()
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
let req = TestRequest::get().uri("/test").to_request();
|
||||
let resp = call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
|
|
56
src/scope.rs
56
src/scope.rs
|
@ -12,7 +12,7 @@ use futures::future::{ok, Either, Future, FutureResult};
|
|||
use futures::{Async, IntoFuture, Poll};
|
||||
|
||||
use crate::config::ServiceConfig;
|
||||
use crate::data::Data;
|
||||
use crate::data::{AppData, Data, DataRaw};
|
||||
use crate::dev::{AppService, HttpServiceFactory};
|
||||
use crate::error::Error;
|
||||
use crate::guard::Guard;
|
||||
|
@ -152,12 +152,19 @@ where
|
|||
self.register_data(Data::new(data))
|
||||
}
|
||||
|
||||
/// The same usage as `Resource::data` method.
|
||||
/// The difference is the `U` must be `Send + Sync + Clone` in order to register as `DataRaw`
|
||||
pub fn data_raw<U: Send + Sync + Clone + 'static>(self, data: U) -> Self {
|
||||
self.register_data(DataRaw::new(data))
|
||||
}
|
||||
|
||||
/// Set or override application data.
|
||||
///
|
||||
/// This method has the same effect as [`Scope::data`](#method.data), except
|
||||
/// that instead of taking a value of some type `T`, it expects a value of
|
||||
/// type `Data<T>`. Use a `Data<T>` extractor to retrieve its value.
|
||||
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
|
||||
/// type `Data<T>` or `DataRaw<T>`. Use a `Data<T>` or `DataRaw<T>` extractor
|
||||
/// to retrieve its value.
|
||||
pub fn register_data<U: AppData + 'static>(mut self, data: U) -> Self {
|
||||
if self.data.is_none() {
|
||||
self.data = Some(Extensions::new());
|
||||
}
|
||||
|
@ -655,10 +662,13 @@ impl NewService for ScopeEndpoint {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use actix_service::Service;
|
||||
use bytes::Bytes;
|
||||
use futures::{Future, IntoFuture};
|
||||
|
||||
use crate::data::AppData;
|
||||
use crate::dev::{Body, ResponseBody};
|
||||
use crate::http::{header, HeaderValue, Method, StatusCode};
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
|
@ -1113,6 +1123,46 @@ mod tests {
|
|||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_override_data_raw() {
|
||||
let mut srv = init_service(App::new().data_raw(Arc::new(Mutex::new(1usize))).service(
|
||||
web::scope("app").data_raw(Arc::new(Mutex::new(10usize))).route(
|
||||
"/t",
|
||||
web::get().to(|data: web::DataRaw<Arc<Mutex<usize>>>| {
|
||||
assert_eq!(*(data.lock().unwrap()), 10);
|
||||
let _ = data.clone();
|
||||
HttpResponse::Ok()
|
||||
}),
|
||||
),
|
||||
));
|
||||
|
||||
let req = TestRequest::with_uri("/app/t").to_request();
|
||||
let resp = call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_override_register_data_raw() {
|
||||
let mut srv = init_service(
|
||||
App::new().register_data(web::DataRaw::new(Arc::new(Mutex::new(1usize)))).service(
|
||||
web::scope("app")
|
||||
.register_data(web::DataRaw::new(Arc::new(Mutex::new(10usize))))
|
||||
.route(
|
||||
"/t",
|
||||
web::get().to(|data: web::DataRaw<Arc<Mutex<usize>>>| {
|
||||
assert_eq!(*(data.lock().unwrap()), 10);
|
||||
let _ = data.clone();
|
||||
HttpResponse::Ok()
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t").to_request();
|
||||
let resp = call_service(&mut srv, req);
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_config() {
|
||||
let mut srv =
|
||||
|
|
|
@ -19,7 +19,7 @@ pub use actix_http::test::TestBuffer;
|
|||
pub use actix_testing::{block_fn, block_on, run_on};
|
||||
|
||||
use crate::config::{AppConfig, AppConfigInner};
|
||||
use crate::data::Data;
|
||||
use crate::data::{AppData, Data};
|
||||
use crate::dev::{Body, MessageBody, Payload};
|
||||
use crate::request::HttpRequestPool;
|
||||
use crate::rmap::ResourceMap;
|
||||
|
@ -521,8 +521,8 @@ mod tests {
|
|||
assert!(req.headers().contains_key(header::DATE));
|
||||
assert_eq!(&req.match_info()["test"], "123");
|
||||
assert_eq!(req.version(), Version::HTTP_2);
|
||||
let data = req.get_app_data::<u32>().unwrap();
|
||||
assert!(req.get_app_data::<u64>().is_none());
|
||||
let data = req.get_app_data::<web::Data<u32>>().unwrap();
|
||||
assert!(req.get_app_data::<web::Data<u64>>().is_none());
|
||||
assert_eq!(*data, 10);
|
||||
assert_eq!(*data.get_ref(), 10);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::scope::Scope;
|
|||
use crate::service::WebService;
|
||||
|
||||
pub use crate::config::ServiceConfig;
|
||||
pub use crate::data::Data;
|
||||
pub use crate::data::{AppData, Data, DataRaw};
|
||||
pub use crate::request::HttpRequest;
|
||||
pub use crate::types::*;
|
||||
|
||||
|
|
Loading…
Reference in a new issue