mirror of https://github.com/fafhrd91/actix-web
WIP: Scope configuarion
This commit is contained in:
parent
f1764bba43
commit
7167f15bc3
|
@ -2,6 +2,8 @@ use std::any::{Any, TypeId};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use hashbrown::hash_map::{Keys, Values};
|
||||||
|
use hashbrown::hash_map::IntoIter;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
/// A type map of request extensions.
|
/// A type map of request extensions.
|
||||||
|
@ -18,6 +20,14 @@ impl Extensions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create `Extensions` with the provided content
|
||||||
|
#[inline]
|
||||||
|
pub fn from(e: Extensions) -> Extensions{
|
||||||
|
Extensions {
|
||||||
|
map: HashMap::from(e.map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Insert a type into this `Extensions`.
|
/// Insert a type into this `Extensions`.
|
||||||
///
|
///
|
||||||
/// If a extension of this type already existed, it will
|
/// If a extension of this type already existed, it will
|
||||||
|
@ -62,6 +72,18 @@ impl Extensions {
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.map.clear();
|
self.map.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_iter(&self) -> IntoIter<TypeId, Box<Any>> {
|
||||||
|
self.map.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keys(& self) -> Keys<TypeId, Box<Any>> {
|
||||||
|
self.map.keys()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values(& self) -> Values<TypeId, Box<Any>> {
|
||||||
|
self.map.values()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Extensions {
|
impl fmt::Debug for Extensions {
|
||||||
|
|
|
@ -15,6 +15,7 @@ use crate::service::{
|
||||||
HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest,
|
HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest,
|
||||||
ServiceResponse,
|
ServiceResponse,
|
||||||
};
|
};
|
||||||
|
use core::borrow::{Borrow, BorrowMut};
|
||||||
|
|
||||||
type Guards = Vec<Box<Guard>>;
|
type Guards = Vec<Box<Guard>>;
|
||||||
type HttpNewService =
|
type HttpNewService =
|
||||||
|
@ -188,7 +189,7 @@ impl ServiceConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set application data. Applicatin data could be accessed
|
/// Set application data. Application data could be accessed
|
||||||
/// by using `Data<T>` extractor where `T` is data type.
|
/// by using `Data<T>` extractor where `T` is data type.
|
||||||
///
|
///
|
||||||
/// This is same as `App::data()` method.
|
/// This is same as `App::data()` method.
|
||||||
|
@ -239,6 +240,58 @@ impl ServiceConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ScopeConfig {
|
||||||
|
pub(crate) services: Vec<Box<ServiceFactory>>,
|
||||||
|
pub(crate) guards: Vec<Box<Guard>>,
|
||||||
|
pub(crate) data: Option<Extensions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScopeConfig {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
services: Vec::new(),
|
||||||
|
guards: Vec::new(),
|
||||||
|
data: Some(Extensions::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set application data. Application data could be accessed
|
||||||
|
/// by using `Data<T>` extractor where `T` is data type.
|
||||||
|
///
|
||||||
|
/// This is same as `App::data()` method.
|
||||||
|
pub fn data<S: 'static>(&mut self, data: S) -> &mut Self {
|
||||||
|
if self.data.is_none() {
|
||||||
|
self.data = Some(Extensions::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data.as_mut().unwrap().insert(data);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure route for a specific path.
|
||||||
|
///
|
||||||
|
/// This is same as `App::route()` method.
|
||||||
|
pub fn route(&mut self, path: &str, mut route: Route) -> &mut Self {
|
||||||
|
self.service(
|
||||||
|
Resource::new(path)
|
||||||
|
.add_guards(route.take_guards())
|
||||||
|
.route(route),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register http service.
|
||||||
|
///
|
||||||
|
/// This is same as `App::service()` method.
|
||||||
|
pub fn service<F>(&mut self, factory: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: HttpServiceFactory + 'static,
|
||||||
|
{
|
||||||
|
self.services
|
||||||
|
.push(Box::new(ServiceFactoryWrapper::new(factory)));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
|
|
73
src/scope.rs
73
src/scope.rs
|
@ -21,6 +21,7 @@ use crate::route::Route;
|
||||||
use crate::service::{
|
use crate::service::{
|
||||||
ServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
|
ServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
|
||||||
};
|
};
|
||||||
|
use crate::config::ScopeConfig;
|
||||||
|
|
||||||
type Guards = Vec<Box<Guard>>;
|
type Guards = Vec<Box<Guard>>;
|
||||||
type HttpService = BoxedService<ServiceRequest, ServiceResponse, Error>;
|
type HttpService = BoxedService<ServiceRequest, ServiceResponse, Error>;
|
||||||
|
@ -83,6 +84,62 @@ impl Scope {
|
||||||
factory_ref: fref,
|
factory_ref: fref,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Run external configuration as part of the scope building
|
||||||
|
/// process
|
||||||
|
///
|
||||||
|
/// This function is useful for moving parts of configuration to a
|
||||||
|
/// different module or even library. For example,
|
||||||
|
/// some of the resource's configuration could be moved to different module.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// use actix_web::{web, middleware, App, HttpResponse};
|
||||||
|
///
|
||||||
|
/// // this function could be located in different module
|
||||||
|
/// fn config(cfg: &mut web::ScopeConfig) {
|
||||||
|
/// cfg.service(web::resource("/test")
|
||||||
|
/// .route(web::get().to(|| HttpResponse::Ok()))
|
||||||
|
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new()
|
||||||
|
/// .wrap(middleware::Logger::default())
|
||||||
|
/// .service(
|
||||||
|
/// web::scope("/api")
|
||||||
|
/// .configure(config)
|
||||||
|
/// )
|
||||||
|
/// .route("/index.html", web::get().to(|| HttpResponse::Ok()));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn configure<F>(mut self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut ScopeConfig),
|
||||||
|
{
|
||||||
|
let mut cfg = ScopeConfig::new();
|
||||||
|
f(&mut cfg);
|
||||||
|
self.services.extend(cfg.services);
|
||||||
|
self.guards.extend(cfg.guards);
|
||||||
|
//self.data.extend(cfg.data);
|
||||||
|
|
||||||
|
if cfg.data.is_some() {
|
||||||
|
if (&self).data.is_some() {
|
||||||
|
|
||||||
|
let mut new_data = Extensions::from(cfg.data.unwrap());
|
||||||
|
|
||||||
|
let old_data = Extensions::from(self.data.take().unwrap());
|
||||||
|
for value in old_data.into_iter() {
|
||||||
|
new_data.insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data = Some(new_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Scope<T>
|
impl<T> Scope<T>
|
||||||
|
@ -1022,4 +1079,20 @@ mod tests {
|
||||||
let resp = call_service(&mut srv, req);
|
let resp = call_service(&mut srv, req);
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scope_config() {
|
||||||
|
let mut srv = init_service(
|
||||||
|
App::new().service(
|
||||||
|
web::scope("/app")
|
||||||
|
.configure(||{
|
||||||
|
|
||||||
|
})
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/app/path1").to_request();
|
||||||
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue