diff --git a/CHANGES.md b/CHANGES.md index d99aa8ba2..b8ab0f879 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,9 @@ * Added the ability to pass a custom `TlsConnector`. +* Allow to register handlers on scope level #465 + + ### Fixed * Handle socket read disconnect diff --git a/src/scope.rs b/src/scope.rs index 8298f534a..83e43f43a 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -5,7 +5,10 @@ use std::rc::Rc; use futures::{Async, Future, Poll}; use error::Error; -use handler::{AsyncResult, AsyncResultItem, FromRequest, Responder, RouteHandler}; +use handler::{ + AsyncResult, AsyncResultItem, FromRequest, Handler, Responder, RouteHandler, + WrapHandler, +}; use http::Method; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -286,6 +289,44 @@ impl Scope { self } + /// Configure handler for specific path prefix. + /// + /// A path prefix consists of valid path segments, i.e for the + /// prefix `/app` any request with the paths `/app`, `/app/` or + /// `/app/test` would match, but the path `/application` would + /// not. + /// + /// ```rust + /// # extern crate actix_web; + /// use actix_web::{http, App, HttpRequest, HttpResponse}; + /// + /// fn main() { + /// let app = App::new().scope("/scope-prefix", |scope| { + /// handler("/app", |req: &HttpRequest| match *req.method() { + /// http::Method::GET => HttpResponse::Ok(), + /// http::Method::POST => HttpResponse::MethodNotAllowed(), + /// _ => HttpResponse::NotFound(), + /// }) + /// }); + /// } + /// ``` + pub fn handler>(mut self, path: &str, handler: H) -> Scope { + { + let mut path = path.trim().trim_right_matches('/').to_owned(); + if !path.is_empty() && !path.starts_with('/') { + path.insert(0, '/') + } + if path.len() > 1 && path.ends_with('/') { + path.pop(); + } + + Rc::get_mut(&mut self.router) + .expect("Multiple copies of scope router") + .register_handler(&path, Box::new(WrapHandler::new(handler)), None); + } + self + } + /// Register a scope middleware /// /// This is similar to `App's` middlewares, but @@ -1120,4 +1161,32 @@ mod tests { let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); } + + #[test] + fn test_handler() { + let app = App::new() + .scope("/scope", |scope| { + scope.handler("/test", |_: &_| HttpResponse::Ok()) + }).finish(); + + let req = TestRequest::with_uri("/scope/test").request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/scope/test/").request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/scope/test/app").request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/scope/testapp").request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/scope/blah").request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); + } }