mirror of
https://github.com/actix/actix-web.git
synced 2024-12-22 16:16:40 +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
|
||||
|
||||
## [0.7.5] - 2018-09-xx
|
||||
## [0.7.5] - 2018-09-02
|
||||
|
||||
### Added
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
|||
|
||||
* Handle socket read disconnect
|
||||
|
||||
* Handling scoped paths without leading slashes #460
|
||||
|
||||
|
||||
## [0.7.4] - 2018-08-23
|
||||
|
||||
|
|
84
src/scope.rs
84
src/scope.rs
|
@ -183,7 +183,7 @@ impl<S: 'static> Scope<S> {
|
|||
where
|
||||
F: FnOnce(Scope<S>) -> Scope<S>,
|
||||
{
|
||||
let rdef = ResourceDef::prefix(&path);
|
||||
let rdef = ResourceDef::prefix(&insert_slash(path));
|
||||
let scope = Scope {
|
||||
rdef: rdef.clone(),
|
||||
filters: Vec::new(),
|
||||
|
@ -230,9 +230,11 @@ impl<S: 'static> Scope<S> {
|
|||
R: Responder + 'static,
|
||||
T: FromRequest<S> + 'static,
|
||||
{
|
||||
Rc::get_mut(&mut self.router)
|
||||
.unwrap()
|
||||
.register_route(path, method, f);
|
||||
Rc::get_mut(&mut self.router).unwrap().register_route(
|
||||
&insert_slash(path),
|
||||
method,
|
||||
f,
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -264,7 +266,7 @@ impl<S: 'static> Scope<S> {
|
|||
F: FnOnce(&mut Resource<S>) -> R + 'static,
|
||||
{
|
||||
// add resource
|
||||
let mut resource = Resource::new(ResourceDef::new(path));
|
||||
let mut resource = Resource::new(ResourceDef::new(&insert_slash(path)));
|
||||
f(&mut resource);
|
||||
|
||||
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> {
|
||||
{
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
let tail = req.match_info().tail as usize;
|
||||
|
@ -844,6 +852,34 @@ mod tests {
|
|||
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]
|
||||
fn test_scope_filter() {
|
||||
let app = App::new()
|
||||
|
@ -1013,6 +1049,20 @@ mod tests {
|
|||
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]
|
||||
fn test_nested_scope_root() {
|
||||
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")
|
||||
.and_then(|(sock, _)| tokio::io::read_exact(sock, &mut buf))
|
||||
.and_then(|(_, buf)| Ok(buf))
|
||||
})
|
||||
.map_err(|e| panic!("{:?}", e));
|
||||
}).map_err(|e| panic!("{:?}", e));
|
||||
let response = srv.execute(request).unwrap();
|
||||
let rep = String::from_utf8_lossy(&response[..]);
|
||||
assert!(rep.contains("HTTP/1.1 404 Not Found"));
|
||||
|
|
Loading…
Reference in a new issue