Align BasicErrorController’s HTML response status with non-HTML status

Previously, BasicErrorController would return the response status
set in the javax.servlet.error.status_code request attribute when
serving JSON but would also return a 200 OK response when serving
HTML. This didn’t cause much trouble when a person was browsing, but
proved problematic for machine clients that request text/html and care
about the response status. For example, the success handler would be
driven for an XHR request even though the response was really an error.

This commit updates BasicErrorController to set the response status for
text/html responses to match the status that it would use in an
application/json response.

Closes gh-4694
This commit is contained in:
Andy Wilkinson 2015-12-10 13:40:16 +00:00
parent b36fe2cbd8
commit ce541bebcf
5 changed files with 11 additions and 8 deletions

View File

@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.web;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
@ -61,7 +62,9 @@ public class BasicErrorController implements ErrorController {
} }
@RequestMapping(value = "${error.path:/error}", produces = "text/html") @RequestMapping(value = "${error.path:/error}", produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request) { public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
response.setStatus(getStatus(request).value());
return new ModelAndView("error", getErrorAttributes(request, false)); return new ModelAndView("error", getErrorAttributes(request, false));
} }

View File

@ -85,7 +85,7 @@ public class BasicErrorControllerDirectMockMvcTests {
.run("--server.port=0")); .run("--server.port=0"));
MvcResult response = this.mockMvc MvcResult response = this.mockMvc
.perform(get("/error").accept(MediaType.TEXT_HTML)) .perform(get("/error").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn(); .andExpect(status().is5xxServerError()).andReturn();
String content = response.getResponse().getContentAsString(); String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("status=999")); assertTrue("Wrong content: " + content, content.contains("status=999"));
} }
@ -96,7 +96,7 @@ public class BasicErrorControllerDirectMockMvcTests {
WebMvcIncludedConfiguration.class).run("--server.port=0")); WebMvcIncludedConfiguration.class).run("--server.port=0"));
MvcResult response = this.mockMvc MvcResult response = this.mockMvc
.perform(get("/error").accept(MediaType.TEXT_HTML)) .perform(get("/error").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn(); .andExpect(status().is5xxServerError()).andReturn();
String content = response.getResponse().getContentAsString(); String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("status=999")); assertTrue("Wrong content: " + content, content.contains("status=999"));
} }
@ -116,7 +116,7 @@ public class BasicErrorControllerDirectMockMvcTests {
WithAopConfiguration.class).run("--server.port=0")); WithAopConfiguration.class).run("--server.port=0"));
MvcResult response = this.mockMvc MvcResult response = this.mockMvc
.perform(get("/error").accept(MediaType.TEXT_HTML)) .perform(get("/error").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn(); .andExpect(status().is5xxServerError()).andReturn();
String content = response.getResponse().getContentAsString(); String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("status=999")); assertTrue("Wrong content: " + content, content.contains("status=999"));
} }

View File

@ -121,7 +121,7 @@ public class BasicErrorControllerMockMvcTests {
public void testDirectAccessForBrowserClient() throws Exception { public void testDirectAccessForBrowserClient() throws Exception {
MvcResult response = this.mockMvc MvcResult response = this.mockMvc
.perform(get("/error").accept(MediaType.TEXT_HTML)) .perform(get("/error").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn(); .andExpect(status().is5xxServerError()).andReturn();
String content = response.getResponse().getContentAsString(); String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("ERROR_BEAN")); assertTrue("Wrong content: " + content, content.contains("ERROR_BEAN"));
} }

View File

@ -69,7 +69,7 @@ public class DefaultErrorViewIntegrationTests {
public void testErrorForBrowserClient() throws Exception { public void testErrorForBrowserClient() throws Exception {
MvcResult response = this.mockMvc MvcResult response = this.mockMvc
.perform(get("/error").accept(MediaType.TEXT_HTML)) .perform(get("/error").accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn(); .andExpect(status().is5xxServerError()).andReturn();
String content = response.getResponse().getContentAsString(); String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("<html>")); assertTrue("Wrong content: " + content, content.contains("<html>"));
assertTrue("Wrong content: " + content, content.contains("999")); assertTrue("Wrong content: " + content, content.contains("999"));
@ -83,7 +83,7 @@ public class DefaultErrorViewIntegrationTests {
new RuntimeException( new RuntimeException(
"<script>alert('Hello World')</script>")) "<script>alert('Hello World')</script>"))
.accept(MediaType.TEXT_HTML)) .accept(MediaType.TEXT_HTML))
.andExpect(status().isOk()).andReturn(); .andExpect(status().is5xxServerError()).andReturn();
String content = response.getResponse().getContentAsString(); String content = response.getResponse().getContentAsString();
assertTrue("Wrong content: " + content, content.contains("&lt;script&gt;")); assertTrue("Wrong content: " + content, content.contains("&lt;script&gt;"));
assertTrue("Wrong content: " + content, content.contains("Hello World")); assertTrue("Wrong content: " + content, content.contains("Hello World"));

View File

@ -89,7 +89,7 @@ public class SampleActuatorUiApplicationTests {
ResponseEntity<String> entity = new TestRestTemplate().exchange( ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/error", HttpMethod.GET, "http://localhost:" + this.port + "/error", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class); new HttpEntity<Void>(headers), String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode()); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, entity.getStatusCode());
assertTrue("Wrong body:\n" + entity.getBody(), assertTrue("Wrong body:\n" + entity.getBody(),
entity.getBody().contains("<html>")); entity.getBody().contains("<html>"));
assertTrue("Wrong body:\n" + entity.getBody(), assertTrue("Wrong body:\n" + entity.getBody(),