From 1fce4876f38b9f2ab276bac2c4be7a0762ce8ac2 Mon Sep 17 00:00:00 2001 From: Denys Vitali Date: Mon, 3 Jun 2019 19:12:37 +0200 Subject: [PATCH] Scope configuration (#880) * WIP: Scope configuarion * Extensions: Fix into_iter() * Scope: Fix tests * Add ScopeConfig to web Committing from mobile, if this doesn't look good it's because I haven't tested it... * Scope Config: Use ServiceConfig instead * Scope: Switch to ServiceConfig in doc * ScopeConfig: Remove unnecessary changes, handle the case when data is empty * ScopeConfig: Remove changes from actix-http --- src/config.rs | 2 +- src/scope.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index bc33da9df..8de43f36c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -188,7 +188,7 @@ impl ServiceConfig { } } - /// Set application data. Applicatin data could be accessed + /// Set application data. Application data could be accessed /// by using `Data` extractor where `T` is data type. /// /// This is same as `App::data()` method. diff --git a/src/scope.rs b/src/scope.rs index 84f34dae6..e9b60c6e3 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -21,6 +21,7 @@ use crate::route::Route; use crate::service::{ ServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse, }; +use crate::config::ServiceConfig; type Guards = Vec>; type HttpService = BoxedService; @@ -83,6 +84,56 @@ impl Scope { 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::ServiceConfig) { + /// 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(mut self, f: F) -> Self + where + F: FnOnce(&mut ServiceConfig), + { + let mut cfg = ServiceConfig::new(); + f(&mut cfg); + self.services.extend(cfg.services); + + if !cfg.data.is_empty() { + let mut data = self.data.unwrap_or(Extensions::new()); + + for value in cfg.data.iter() { + value.create(&mut data); + } + + self.data = Some(data); + } + self + } } impl Scope @@ -1022,4 +1073,40 @@ mod tests { let resp = call_service(&mut srv, req); assert_eq!(resp.status(), StatusCode::OK); } + + #[test] + fn test_scope_config() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .configure(|s|{ + s.route("/path1", web::get().to(||HttpResponse::Ok())); + }) + ), + ); + + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + + #[test] + fn test_scope_config_2() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .configure(|s|{ + s.service( + web::scope("/v1") + .configure(|s|{ + s.route("/", web::get().to(||HttpResponse::Ok())); + })); + }) + ), + ); + + let req = TestRequest::with_uri("/app/v1/").to_request(); + let resp = block_on(srv.call(req)).unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } }