Skip to content

Commit 244c95b

Browse files
committed
Fix @RequestBody argument processing for null Content-Type
Since the changes introduced in SPR-12778, some `@RequestBody` args would not be properly processed in some cases: * requests with an empty body * no Content-Type header defined This typically happens when GET requests are mapped on a handler dealing with POST requests and HTTP bodies. This change makes sure that the `RequestResponseBodyMethodProcessor` is only involved for requests that: * have a Content-Type defined * OR are HTTP requests eligible for an HTTP body (PUT, POST, PATCH) Issue: SPR-13176 Fixes spring-projects/spring-boot#3313
1 parent 749bee4 commit 244c95b

File tree

4 files changed

+30
-17
lines changed

4 files changed

+30
-17
lines changed

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818

1919
import java.io.IOException;
2020
import java.lang.reflect.Type;
21+
import java.util.Arrays;
2122
import java.util.List;
2223

2324
import javax.servlet.http.HttpServletRequest;
2425

2526
import org.springframework.core.Conventions;
2627
import org.springframework.core.MethodParameter;
2728
import org.springframework.core.annotation.AnnotationUtils;
28-
import org.springframework.http.HttpInputMessage;
29+
import org.springframework.http.HttpMethod;
2930
import org.springframework.http.converter.HttpMessageConverter;
3031
import org.springframework.http.converter.HttpMessageNotReadableException;
3132
import org.springframework.http.server.ServletServerHttpRequest;
@@ -58,6 +59,9 @@
5859
*/
5960
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
6061

62+
private static final List<HttpMethod> SUPPORTED_METHODS =
63+
Arrays.asList(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
64+
6165
/**
6266
* Basic constructor with converters only. Suitable for resolving
6367
* {@code @RequestBody}. For handling {@code @ResponseBody} consider also
@@ -143,16 +147,19 @@ protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, Meth
143147
Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
144148

145149
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
146-
HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest);
147-
148-
Object arg = readWithMessageConverters(inputMessage, methodParam, paramType);
149-
if (arg == null) {
150-
if (methodParam.getParameterAnnotation(RequestBody.class).required()) {
151-
throw new HttpMessageNotReadableException("Required request body is missing: " +
152-
methodParam.getMethod().toGenericString());
150+
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
151+
152+
Object arg = null;
153+
if (webRequest.getHeader("Content-Type") != null ||
154+
SUPPORTED_METHODS.contains(inputMessage.getMethod())) {
155+
arg = readWithMessageConverters(inputMessage, methodParam, paramType);
156+
if (arg == null) {
157+
if (methodParam.getParameterAnnotation(RequestBody.class).required()) {
158+
throw new HttpMessageNotReadableException("Required request body is missing: " +
159+
methodParam.getMethod().toGenericString());
160+
}
153161
}
154162
}
155-
156163
return arg;
157164
}
158165

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ public void handleRequestBody() throws Exception {
212212

213213
Class<?>[] parameterTypes = new Class<?>[] { byte[].class };
214214

215+
request.setMethod("POST");
215216
request.addHeader("Content-Type", "text/plain; charset=utf-8");
216217
request.setContent("Hello Server".getBytes("UTF-8"));
217218

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public void setUp() throws Exception {
102102
mavContainer = new ModelAndViewContainer();
103103

104104
servletRequest = new MockHttpServletRequest();
105+
servletRequest.setMethod("POST");
105106
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
106107
}
107108

@@ -178,17 +179,11 @@ public void resolveArgumentCannotRead() throws Exception {
178179
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
179180
}
180181

181-
@Test
182+
@Test(expected = HttpMediaTypeNotSupportedException.class)
182183
public void resolveArgumentNoContentType() throws Exception {
183184
servletRequest.setContent("payload".getBytes(Charset.forName("UTF-8")));
184185
given(messageConverter.canRead(String.class, MediaType.APPLICATION_OCTET_STREAM)).willReturn(false);
185-
try {
186-
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
187-
fail("Expected exception");
188-
}
189-
catch (HttpMediaTypeNotSupportedException ex) {
190-
// expected
191-
}
186+
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
192187
}
193188

194189
@Test(expected = HttpMediaTypeNotSupportedException.class)
@@ -217,6 +212,14 @@ public void resolveArgumentNotRequiredNoContent() throws Exception {
217212
assertNull(processor.resolveArgument(paramStringNotRequired, mavContainer, webRequest, new ValidatingBinderFactory()));
218213
}
219214

215+
@Test
216+
public void resolveArgumentNotGetRequests() throws Exception {
217+
servletRequest.setMethod("GET");
218+
servletRequest.setContent(new byte[0]);
219+
given(messageConverter.canRead(String.class, MediaType.APPLICATION_OCTET_STREAM)).willReturn(false);
220+
assertNull(processor.resolveArgument(paramStringNotRequired, mavContainer, webRequest, new ValidatingBinderFactory()));
221+
}
222+
220223
@Test
221224
public void handleReturnValue() throws Exception {
222225
MediaType accepted = MediaType.TEXT_PLAIN;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public void setUp() throws Exception {
112112
mavContainer = new ModelAndViewContainer();
113113

114114
servletRequest = new MockHttpServletRequest();
115+
servletRequest.setMethod("POST");
115116
servletResponse = new MockHttpServletResponse();
116117
webRequest = new ServletWebRequest(servletRequest, servletResponse);
117118

@@ -140,6 +141,7 @@ public void resolveArgumentParameterizedType() throws Exception {
140141
@Test
141142
public void resolveArgumentRawTypeFromParameterizedType() throws Exception {
142143
String content = "fruit=apple&vegetable=kale";
144+
this.servletRequest.setMethod("GET");
143145
this.servletRequest.setContent(content.getBytes("UTF-8"));
144146
this.servletRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
145147

0 commit comments

Comments
 (0)