Skip to content

Commit d52fc3b

Browse files
committed
Prevent response updates if @ResponseStatus has reason
When @ResponseStatus has a reason and servletResponse.sendError() is called, the response is committed and should no longer be written to. After this change, the ServletInvocableHandlerMethod will mark the response fully handled and will ignore any non-null return values. Issue: SPR-9159
1 parent 0b17dd2 commit d52fc3b

File tree

4 files changed

+32
-11
lines changed

4 files changed

+32
-11
lines changed

spring-web/src/main/java/org/springframework/web/bind/annotation/ResponseStatus.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 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.
@@ -46,7 +46,8 @@
4646

4747
/**
4848
* The reason to be used for the response. <p>If this element is not set, it will default to the standard status
49-
* message for the status code.
49+
* message for the status code. Note that due to the use of {@code HttpServletResponse.sendError(int, String)},
50+
* the response will be considered complete and should not be written to any further.
5051
*
5152
* @see javax.servlet.http.HttpServletResponse#sendError(int, String)
5253
*/

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ public final void invokeAndHandle(ServletWebRequest webRequest,
104104
return;
105105
}
106106
}
107+
else if (StringUtils.hasText(this.responseReason)) {
108+
mavContainer.setRequestHandled(true);
109+
return;
110+
}
107111

108112
mavContainer.setRequestHandled(false);
109113

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,17 @@ public void setUp() throws Exception {
7272
}
7373

7474
@Test
75-
public void nullReturnValueResponseStatus() throws Exception {
75+
public void invokeAndHandle_VoidWithResponseStatus() throws Exception {
7676
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("responseStatus");
7777
handlerMethod.invokeAndHandle(webRequest, mavContainer);
7878

7979
assertTrue("Null return value + @ResponseStatus should result in 'request handled'",
8080
mavContainer.isRequestHandled());
81-
8281
assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
83-
assertEquals("400 Bad Request", response.getErrorMessage());
8482
}
8583

8684
@Test
87-
public void nullReturnValueHttpServletResponseArg() throws Exception {
85+
public void invokeAndHandle_VoidWithHttpServletResponseArgument() throws Exception {
8886
argumentResolvers.addResolver(new ServletResponseMethodArgumentResolver());
8987

9088
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("httpServletResponse", HttpServletResponse.class);
@@ -95,7 +93,7 @@ public void nullReturnValueHttpServletResponseArg() throws Exception {
9593
}
9694

9795
@Test
98-
public void nullReturnValueRequestNotModified() throws Exception {
96+
public void invokeAndHandle_VoidRequestNotModified() throws Exception {
9997
webRequest.getNativeRequest(MockHttpServletRequest.class).addHeader("If-Modified-Since", 10 * 1000 * 1000);
10098
int lastModifiedTimestamp = 1000 * 1000;
10199
webRequest.checkNotModified(lastModifiedTimestamp);
@@ -107,8 +105,20 @@ public void nullReturnValueRequestNotModified() throws Exception {
107105
mavContainer.isRequestHandled());
108106
}
109107

108+
// SPR-9159
109+
110+
@Test
111+
public void invokeAndHandle_NotVoidWithResponseStatusAndReason() throws Exception {
112+
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("responseStatusWithReason");
113+
handlerMethod.invokeAndHandle(webRequest, mavContainer);
114+
115+
assertTrue("When a phrase is used, the response should not be used any more", mavContainer.isRequestHandled());
116+
assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus());
117+
assertEquals("400 Bad Request", response.getErrorMessage());
118+
}
119+
110120
@Test(expected=HttpMessageNotWritableException.class)
111-
public void exceptionWhileHandlingReturnValue() throws Exception {
121+
public void invokeAndHandle_Exception() throws Exception {
112122
returnValueHandlers.addHandler(new ExceptionRaisingReturnValueHandler());
113123

114124
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("handle");
@@ -117,7 +127,7 @@ public void exceptionWhileHandlingReturnValue() throws Exception {
117127
}
118128

119129
@Test
120-
public void dynamicReturnValue() throws Exception {
130+
public void invokeAndHandle_DynamicReturnValue() throws Exception {
121131
argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(null, false));
122132
returnValueHandlers.addHandler(new ViewMethodReturnValueHandler());
123133
returnValueHandlers.addHandler(new ViewNameMethodReturnValueHandler());
@@ -153,10 +163,15 @@ public String handle() {
153163
return "view";
154164
}
155165

156-
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "400 Bad Request")
166+
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
157167
public void responseStatus() {
158168
}
159169

170+
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "400 Bad Request")
171+
public String responseStatusWithReason() {
172+
return "foo";
173+
}
174+
160175
public void httpServletResponse(HttpServletResponse response) {
161176
}
162177

src/dist/changelog.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ Changes in version 3.2 M1
1414
* fix case-sensitivity issue with some containers on access to 'Content-Disposition' header
1515
* add Servlet 3.0 based async support
1616
* fix issue with encoded params in UriComponentsBuilder
17-
* add Jackson 2 HttpMessageConverter and View types
17+
* add HttpMessageConverter and View types compatible with Jackson 2.0
1818
* add pretty print option to Jackson HttpMessageConverter and View types
1919
* fix issue with resolving Errors controller method argument
2020
* detect controller methods via InitializingBean in RequestMappingHandlerMapping
2121
* translate IOException from Jackson to HttpMessageNotReadableException
2222
* fix content negotiation issue when sorting selected media types by quality value
23+
* Prevent further writing to the response when @ResponseStatus contains a reason
2324

2425

2526
Changes in version 3.1.1 (2012-02-16)

0 commit comments

Comments
 (0)