Add generic error handling to BasicErrorController

Since Spring supports gobal error handling through
@ControllerAdvice, it is quite easy to set up more meta-data
about an exception for the BasicErrorController. You need
to be careful not to swallow Security exceptions, and probably
others (optionally) so this feature needs a bit more work.

See gh-538
This commit is contained in:
Dave Syer
2014-04-11 16:46:09 +01:00
parent 4474b104b6
commit 27580e726f
5 changed files with 190 additions and 6 deletions

View File

@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.web;
import java.util.Map;
import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -35,11 +37,16 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.AbstractView;
@@ -67,7 +74,7 @@ public class BasicErrorControllerIntegrationTests {
}
@Test
public void testErrorForMachineClient() throws Exception {
public void testDirectAccessForMachineClient() throws Exception {
MvcResult response = this.mockMvc.perform(get("/error"))
.andExpect(status().is5xxServerError()).andReturn();
String content = response.getResponse().getContentAsString();
@@ -75,7 +82,32 @@ public class BasicErrorControllerIntegrationTests {
}
@Test
public void testErrorForBrowserClient() throws Exception {
public void testErrorForMachineClient() throws Exception {
MvcResult result = this.mockMvc.perform(get("/"))
.andExpect(status().is5xxServerError()).andReturn();
MvcResult response = this.mockMvc.perform(new ErrorDispatcher(result, "/error"))
.andReturn();
String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("Expected!"));
}
@Test
public void testBindingExceptionForMachineClient() throws Exception {
// In a real container the response is carried over into the error dispatcher, but
// in the mock a new one is created so we have to assert the status at this
// intermediate point
MvcResult result = this.mockMvc.perform(get("/bind"))
.andExpect(status().is4xxClientError()).andReturn();
MvcResult response = this.mockMvc.perform(new ErrorDispatcher(result, "/error"))
.andReturn();
// And the rendered status code is always wrong (but would be 400 in a real
// system)
String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("Error count: 1"));
}
@Test
public void testDirectAccessForBrowserClient() throws Exception {
MvcResult response = this.mockMvc
.perform(get("/error").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn();
@@ -106,6 +138,46 @@ public class BasicErrorControllerIntegrationTests {
};
}
@RestController
protected static class Errors {
public String getFoo() {
return "foo";
}
@RequestMapping("/")
public String home() {
throw new IllegalStateException("Expected!");
}
@RequestMapping("/bind")
public String bind() throws Exception {
BindException error = new BindException(this, "test");
error.rejectValue("foo", "bar.error");
throw error;
}
}
}
private class ErrorDispatcher implements RequestBuilder {
private MvcResult result;
private String path;
public ErrorDispatcher(MvcResult result, String path) {
this.result = result;
this.path = path;
}
@Override
public MockHttpServletRequest buildRequest(ServletContext servletContext) {
MockHttpServletRequest request = this.result.getRequest();
request.setDispatcherType(DispatcherType.ERROR);
request.setRequestURI(this.path);
return request;
}
}
}