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
master
Brian Clozel 9 years ago
parent 749bee4d58
commit 244c95b076
  1. 25
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java
  2. 1
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java
  3. 19
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorMockTests.java
  4. 2
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java

@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
@ -25,7 +26,7 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.core.Conventions;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServletServerHttpRequest;
@ -58,6 +59,9 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
*/
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
private static final List<HttpMethod> SUPPORTED_METHODS =
Arrays.asList(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
/**
* Basic constructor with converters only. Suitable for resolving
* {@code @RequestBody}. For handling {@code @ResponseBody} consider also
@ -143,16 +147,19 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest);
Object arg = readWithMessageConverters(inputMessage, methodParam, paramType);
if (arg == null) {
if (methodParam.getParameterAnnotation(RequestBody.class).required()) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
methodParam.getMethod().toGenericString());
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
Object arg = null;
if (webRequest.getHeader("Content-Type") != null ||
SUPPORTED_METHODS.contains(inputMessage.getMethod())) {
arg = readWithMessageConverters(inputMessage, methodParam, paramType);
if (arg == null) {
if (methodParam.getParameterAnnotation(RequestBody.class).required()) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
methodParam.getMethod().toGenericString());
}
}
}
return arg;
}

@ -212,6 +212,7 @@ public class RequestMappingHandlerAdapterIntegrationTests {
Class<?>[] parameterTypes = new Class<?>[] { byte[].class };
request.setMethod("POST");
request.addHeader("Content-Type", "text/plain; charset=utf-8");
request.setContent("Hello Server".getBytes("UTF-8"));

@ -102,6 +102,7 @@ public class RequestResponseBodyMethodProcessorMockTests {
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest();
servletRequest.setMethod("POST");
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
}
@ -178,17 +179,11 @@ public class RequestResponseBodyMethodProcessorMockTests {
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
}
@Test
@Test(expected = HttpMediaTypeNotSupportedException.class)
public void resolveArgumentNoContentType() throws Exception {
servletRequest.setContent("payload".getBytes(Charset.forName("UTF-8")));
given(messageConverter.canRead(String.class, MediaType.APPLICATION_OCTET_STREAM)).willReturn(false);
try {
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
fail("Expected exception");
}
catch (HttpMediaTypeNotSupportedException ex) {
// expected
}
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
}
@Test(expected = HttpMediaTypeNotSupportedException.class)
@ -217,6 +212,14 @@ public class RequestResponseBodyMethodProcessorMockTests {
assertNull(processor.resolveArgument(paramStringNotRequired, mavContainer, webRequest, new ValidatingBinderFactory()));
}
@Test
public void resolveArgumentNotGetRequests() throws Exception {
servletRequest.setMethod("GET");
servletRequest.setContent(new byte[0]);
given(messageConverter.canRead(String.class, MediaType.APPLICATION_OCTET_STREAM)).willReturn(false);
assertNull(processor.resolveArgument(paramStringNotRequired, mavContainer, webRequest, new ValidatingBinderFactory()));
}
@Test
public void handleReturnValue() throws Exception {
MediaType accepted = MediaType.TEXT_PLAIN;

@ -112,6 +112,7 @@ public class RequestResponseBodyMethodProcessorTests {
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest();
servletRequest.setMethod("POST");
servletResponse = new MockHttpServletResponse();
webRequest = new ServletWebRequest(servletRequest, servletResponse);
@ -140,6 +141,7 @@ public class RequestResponseBodyMethodProcessorTests {
@Test
public void resolveArgumentRawTypeFromParameterizedType() throws Exception {
String content = "fruit=apple&vegetable=kale";
this.servletRequest.setMethod("GET");
this.servletRequest.setContent(content.getBytes("UTF-8"));
this.servletRequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);

Loading…
Cancel
Save