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:
parent
d5957a8466
commit
968c81e267
3 changed files with 71 additions and 20 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
84
src/scope.rs
84
src/scope.rs
|
@ -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()
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
Loading…
Reference in a new issue