1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-12-23 00:26:34 +00:00

Handling scoped paths without leading slashes #460

This commit is contained in:
Nikolay Kim 2018-09-02 08:14:54 -07:00
parent d5957a8466
commit 968c81e267
3 changed files with 71 additions and 20 deletions

View file

@ -1,6 +1,6 @@
# Changes # Changes
## [0.7.5] - 2018-09-xx ## [0.7.5] - 2018-09-02
### Added ### Added
@ -13,6 +13,8 @@
* Handle socket read disconnect * Handle socket read disconnect
* Handling scoped paths without leading slashes #460
## [0.7.4] - 2018-08-23 ## [0.7.4] - 2018-08-23

View file

@ -183,7 +183,7 @@ impl<S: 'static> Scope<S> {
where where
F: FnOnce(Scope<S>) -> Scope<S>, F: FnOnce(Scope<S>) -> Scope<S>,
{ {
let rdef = ResourceDef::prefix(&path); let rdef = ResourceDef::prefix(&insert_slash(path));
let scope = Scope { let scope = Scope {
rdef: rdef.clone(), rdef: rdef.clone(),
filters: Vec::new(), filters: Vec::new(),
@ -230,9 +230,11 @@ impl<S: 'static> Scope<S> {
R: Responder + 'static, R: Responder + 'static,
T: FromRequest<S> + 'static, T: FromRequest<S> + 'static,
{ {
Rc::get_mut(&mut self.router) Rc::get_mut(&mut self.router).unwrap().register_route(
.unwrap() &insert_slash(path),
.register_route(path, method, f); method,
f,
);
self self
} }
@ -264,7 +266,7 @@ impl<S: 'static> Scope<S> {
F: FnOnce(&mut Resource<S>) -> R + 'static, F: FnOnce(&mut Resource<S>) -> R + 'static,
{ {
// add resource // add resource
let mut resource = Resource::new(ResourceDef::new(path)); let mut resource = Resource::new(ResourceDef::new(&insert_slash(path)));
f(&mut resource); f(&mut resource);
Rc::get_mut(&mut self.router) Rc::get_mut(&mut self.router)
@ -311,19 +313,17 @@ impl<S: 'static> Scope<S> {
/// } /// }
/// ``` /// ```
pub fn handler<H: Handler<S>>(mut self, path: &str, handler: H) -> Scope<S> { pub fn handler<H: Handler<S>>(mut self, path: &str, handler: H) -> Scope<S> {
{ let mut path = path.trim().trim_right_matches('/').to_owned();
let mut path = path.trim().trim_right_matches('/').to_owned(); if !path.is_empty() && !path.starts_with('/') {
if !path.is_empty() && !path.starts_with('/') { path.insert(0, '/')
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);
} }
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 self
} }
@ -342,6 +342,14 @@ impl<S: 'static> Scope<S> {
} }
} }
fn insert_slash(path: &str) -> String {
let mut path = path.to_owned();
if !path.is_empty() && !path.starts_with('/') {
path.insert(0, '/');
};
path
}
impl<S: 'static> RouteHandler<S> for Scope<S> { impl<S: 'static> RouteHandler<S> for Scope<S> {
fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> { fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
let tail = req.match_info().tail as usize; let tail = req.match_info().tail as usize;
@ -844,6 +852,34 @@ mod tests {
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
#[test]
fn test_scope_route_without_leading_slash() {
let app = App::new()
.scope("app", |scope| {
scope
.route("path1", Method::GET, |_: HttpRequest<_>| HttpResponse::Ok())
.route("path1", Method::DELETE, |_: HttpRequest<_>| {
HttpResponse::Ok()
})
}).finish();
let req = TestRequest::with_uri("/app/path1").request();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1")
.method(Method::DELETE)
.request();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1")
.method(Method::POST)
.request();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
}
#[test] #[test]
fn test_scope_filter() { fn test_scope_filter() {
let app = App::new() let app = App::new()
@ -1013,6 +1049,20 @@ mod tests {
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
} }
#[test]
fn test_nested_scope_no_slash() {
let app = App::new()
.scope("/app", |scope| {
scope.nested("t1", |scope| {
scope.resource("/path1", |r| r.f(|_| HttpResponse::Created()))
})
}).finish();
let req = TestRequest::with_uri("/app/t1/path1").request();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
}
#[test] #[test]
fn test_nested_scope_root() { fn test_nested_scope_root() {
let app = App::new() let app = App::new()

View file

@ -948,8 +948,7 @@ fn test_default_404_handler_response() {
tokio::io::write_all(sock, "HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n") tokio::io::write_all(sock, "HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n")
.and_then(|(sock, _)| tokio::io::read_exact(sock, &mut buf)) .and_then(|(sock, _)| tokio::io::read_exact(sock, &mut buf))
.and_then(|(_, buf)| Ok(buf)) .and_then(|(_, buf)| Ok(buf))
}) }).map_err(|e| panic!("{:?}", e));
.map_err(|e| panic!("{:?}", e));
let response = srv.execute(request).unwrap(); let response = srv.execute(request).unwrap();
let rep = String::from_utf8_lossy(&response[..]); let rep = String::from_utf8_lossy(&response[..]);
assert!(rep.contains("HTTP/1.1 404 Not Found")); assert!(rep.contains("HTTP/1.1 404 Not Found"));