Skip to content

Commit a06de4d

Browse files
committed
Stop error page filter from commiting response prematurely
Previously, the error page filter used sendError to set the response status when handling an exception and before forwarding the request to the error controller. Following the fix for gh-11814, this meant that the error controller was unable to write its response and the containers default error page was returned instead. This commit updates the error page filter to use setStatus rather than sendError. This ensures that the response has the correct status code while allowing the error controller to write its body. Tests have been added to the Tomcat deployment test suite to verify that the error page filter behaves as intended when dealing with a sent error and an exception for requests accepting HTML, JSON, or anything. Closes gh-12787
1 parent 3634a1d commit a06de4d

File tree

3 files changed

+91
-5
lines changed

3 files changed

+91
-5
lines changed

spring-boot-deployment-tests/spring-boot-deployment-test-tomcat/src/main/java/sample/SampleController.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,10 @@
1616

1717
package sample;
1818

19+
import java.io.IOException;
20+
21+
import javax.servlet.http.HttpServletResponse;
22+
1923
import org.springframework.web.bind.annotation.RequestMapping;
2024
import org.springframework.web.bind.annotation.RestController;
2125

@@ -27,4 +31,14 @@ public String hello() {
2731
return "Hello World";
2832
}
2933

34+
@RequestMapping("/send-error")
35+
public void sendError(HttpServletResponse response) throws IOException {
36+
response.sendError(500);
37+
}
38+
39+
@RequestMapping("/exception")
40+
public void exception() {
41+
throw new RuntimeException();
42+
}
43+
3044
}

spring-boot-deployment-tests/spring-boot-deployment-test-tomcat/src/test/java/sample/SampleTomcatDeployApplicationIT.java

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,14 @@
1616

1717
package sample;
1818

19+
import java.net.URI;
20+
1921
import org.junit.Test;
2022

2123
import org.springframework.boot.test.web.client.TestRestTemplate;
2224
import org.springframework.http.HttpStatus;
25+
import org.springframework.http.MediaType;
26+
import org.springframework.http.RequestEntity;
2327
import org.springframework.http.ResponseEntity;
2428

2529
import static org.assertj.core.api.Assertions.assertThat;
@@ -29,15 +33,83 @@
2933
*/
3034
public class SampleTomcatDeployApplicationIT {
3135

36+
private final TestRestTemplate rest = new TestRestTemplate();
37+
3238
private int port = Integer.valueOf(System.getProperty("port"));
3339

3440
@Test
3541
public void testHome() throws Exception {
3642
String url = "http://localhost:" + this.port + "/bootapp/";
37-
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(url,
38-
String.class);
43+
ResponseEntity<String> entity = this.rest.getForEntity(url, String.class);
3944
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
4045
assertThat(entity.getBody()).isEqualTo("Hello World");
4146
}
4247

48+
@Test
49+
public void errorFromExceptionForRequestAcceptingAnythingProducesAJsonResponse()
50+
throws Exception {
51+
assertThatErrorFromExceptionProducesExpectedResponse(MediaType.ALL,
52+
MediaType.APPLICATION_JSON);
53+
}
54+
55+
@Test
56+
public void errorFromExceptionForRequestAcceptingJsonProducesAJsonResponse()
57+
throws Exception {
58+
assertThatErrorFromExceptionProducesExpectedResponse(MediaType.APPLICATION_JSON,
59+
MediaType.APPLICATION_JSON);
60+
}
61+
62+
@Test
63+
public void errorFromExceptionForRequestAcceptingHtmlProducesAnHtmlResponse()
64+
throws Exception {
65+
assertThatErrorFromExceptionProducesExpectedResponse(MediaType.TEXT_HTML,
66+
MediaType.TEXT_HTML);
67+
}
68+
69+
@Test
70+
public void sendErrorForRequestAcceptingAnythingProducesAJsonResponse()
71+
throws Exception {
72+
assertThatSendErrorProducesExpectedResponse(MediaType.ALL,
73+
MediaType.APPLICATION_JSON);
74+
}
75+
76+
@Test
77+
public void sendErrorForRequestAcceptingJsonProducesAJsonResponse() throws Exception {
78+
assertThatSendErrorProducesExpectedResponse(MediaType.APPLICATION_JSON,
79+
MediaType.APPLICATION_JSON);
80+
}
81+
82+
@Test
83+
public void sendErrorForRequestAcceptingHtmlProducesAnHtmlResponse()
84+
throws Exception {
85+
assertThatSendErrorProducesExpectedResponse(MediaType.TEXT_HTML,
86+
MediaType.TEXT_HTML);
87+
}
88+
89+
private void assertThatSendErrorProducesExpectedResponse(MediaType accept,
90+
MediaType contentType) {
91+
RequestEntity<Void> request = RequestEntity
92+
.get(URI.create("http://localhost:" + this.port + "/bootapp/send-error"))
93+
.accept(accept).build();
94+
ResponseEntity<String> response = this.rest.exchange(request, String.class);
95+
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
96+
assertThat(contentType.isCompatibleWith(response.getHeaders().getContentType()))
97+
.as("%s is compatible with %s", contentType,
98+
response.getHeaders().getContentType())
99+
.isTrue();
100+
}
101+
102+
private void assertThatErrorFromExceptionProducesExpectedResponse(MediaType accept,
103+
MediaType contentType) {
104+
RequestEntity<Void> request = RequestEntity
105+
.get(URI.create("http://localhost:" + this.port + "/bootapp/exception"))
106+
.accept(accept).build();
107+
ResponseEntity<String> response = this.rest.exchange(request, String.class);
108+
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
109+
assertThat(contentType.isCompatibleWith(response.getHeaders().getContentType()))
110+
.as("%s is compatible with %s", contentType,
111+
response.getHeaders().getContentType())
112+
.isTrue();
113+
}
114+
43115
}

spring-boot/src/main/java/org/springframework/boot/web/support/ErrorPageFilter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ private void forwardToErrorPage(String path, HttpServletRequest request,
181181
request.setAttribute(ERROR_EXCEPTION, ex);
182182
request.setAttribute(ERROR_EXCEPTION_TYPE, ex.getClass());
183183
response.reset();
184-
response.sendError(500, ex.getMessage());
184+
response.setStatus(500);
185185
request.getRequestDispatcher(path).forward(request, response);
186186
request.removeAttribute(ERROR_EXCEPTION);
187187
request.removeAttribute(ERROR_EXCEPTION_TYPE);

0 commit comments

Comments
 (0)