Remove HalJsonMvcEndpoint’s redirect and add links to both paths instead

Previously, HalJsonMvcEndpoint used a redirect to go from path/ to path.
When the actuator’s configured to use a custom context path this
redirect was leading to an infinite redirect loop.

This commit removes the redirect in favour of updating the controller
advice to apply the links to requests for path and path/.

Closes gh-4853
This commit is contained in:
Andy Wilkinson 2016-01-18 14:32:01 +00:00
parent c4f756daee
commit 69b60f6d8c
6 changed files with 47 additions and 17 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -196,8 +196,12 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
}
private boolean isActuatorEndpointPath(String path) {
return this.halJsonMvcEndpoint != null && (this.management.getContextPath()
+ this.halJsonMvcEndpoint.getPath()).equals(path);
if (this.halJsonMvcEndpoint != null) {
String toMatch = this.management.getContextPath()
+ this.halJsonMvcEndpoint.getPath();
return toMatch.equals(path) || (toMatch + "/").equals(path);
}
return false;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -87,11 +87,6 @@ public class HalJsonMvcEndpoint extends WebMvcConfigurerAdapter
return new ResourceSupport();
}
@RequestMapping(path = "/", produces = MediaType.APPLICATION_JSON_VALUE)
public String forward() {
return "redirect:" + this.managementServletContext.getContextPath() + this.path;
}
public void setPath(String path) {
this.path = path;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -39,7 +39,6 @@ import org.springframework.web.context.WebApplicationContext;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -77,10 +76,9 @@ public class HalBrowserMvcEndpointManagementContextPathIntegrationTests {
}
@Test
public void redirectJson() throws Exception {
public void actuatorHomeWithTrailingSlashJson() throws Exception {
this.mockMvc.perform(get("/admin/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isFound())
.andExpect(header().string("location", "/admin"));
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -97,6 +97,18 @@ public class HalBrowserMvcEndpointServerContextPathIntegrationTests {
entity.getBody().contains("\"_links\":"));
}
@Test
public void actuatorLinksWithTrailingSlash() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/actuator/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"_links\":"));
}
@MinimalActuatorHypermediaApplication
@RestController
public static class SpringBootHypermediaApplication {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -75,6 +75,20 @@ public class HalBrowserMvcEndpointServerPortIntegrationTests {
entity.getBody().contains(":" + this.port));
}
@Test
public void linksWithTrailingSlash() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/actuator/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"_links\":"));
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains(":" + this.port));
}
@Test
public void browser() throws Exception {
HttpHeaders headers = new HttpHeaders();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -78,6 +78,13 @@ public class HalBrowserMvcEndpointVanillaIntegrationTests {
.andExpect(header().doesNotExist("cache-control"));
}
@Test
public void linksWithTrailingSlash() throws Exception {
this.mockMvc.perform(get("/actuator/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists())
.andExpect(header().doesNotExist("cache-control"));
}
@Test
public void browser() throws Exception {
MvcResult response = this.mockMvc