Skip to content

Commit 3272a3b

Browse files
committed
Check HTTP method before raising 415
This commit moves the check whether an HTTP method supports request body up to the base class so that all sub-classes can benefit (not just @RequestBody). Issue: SPR-13176
1 parent 244c95b commit 3272a3b

File tree

5 files changed

+50
-24
lines changed

5 files changed

+50
-24
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.lang.annotation.Annotation;
2323
import java.lang.reflect.Type;
2424
import java.util.ArrayList;
25+
import java.util.Arrays;
2526
import java.util.Collections;
2627
import java.util.LinkedHashSet;
2728
import java.util.List;
@@ -37,6 +38,8 @@
3738
import org.springframework.core.annotation.AnnotationUtils;
3839
import org.springframework.http.HttpHeaders;
3940
import org.springframework.http.HttpInputMessage;
41+
import org.springframework.http.HttpMethod;
42+
import org.springframework.http.HttpRequest;
4043
import org.springframework.http.InvalidMediaTypeException;
4144
import org.springframework.http.MediaType;
4245
import org.springframework.http.converter.GenericHttpMessageConverter;
@@ -60,6 +63,9 @@
6063
*/
6164
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
6265

66+
private static final List<HttpMethod> SUPPORTED_METHODS =
67+
Arrays.asList(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
68+
6369
private static final Object NO_VALUE = new Object();
6470

6571

@@ -170,6 +176,7 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
170176
targetClass = (Class<T>) resolvableType.resolve();
171177
}
172178

179+
HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
173180
inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);
174181
Object body = NO_VALUE;
175182

@@ -213,6 +220,9 @@ else if (targetClass != null) {
213220
}
214221

215222
if (body == NO_VALUE) {
223+
if (!SUPPORTED_METHODS.contains(httpMethod)) {
224+
return null;
225+
}
216226
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
217227
}
218228

@@ -273,6 +283,8 @@ private static class EmptyBodyCheckingHttpInputMessage implements HttpInputMessa
273283

274284
private final InputStream body;
275285

286+
private final HttpMethod method;
287+
276288

277289
public EmptyBodyCheckingHttpInputMessage(HttpInputMessage inputMessage) throws IOException {
278290
this.headers = inputMessage.getHeaders();
@@ -296,6 +308,7 @@ else if (inputStream.markSupported()) {
296308
pushbackInputStream.unread(b);
297309
}
298310
}
311+
this.method = ((HttpRequest) inputMessage).getMethod();
299312
}
300313

301314
@Override
@@ -307,6 +320,10 @@ public HttpHeaders getHeaders() {
307320
public InputStream getBody() throws IOException {
308321
return this.body;
309322
}
323+
324+
public HttpMethod getMethod() {
325+
return this.method;
326+
}
310327
}
311328

312329
}

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,11 @@ protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, Meth
150150
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
151151

152152
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-
}
153+
arg = readWithMessageConverters(inputMessage, methodParam, paramType);
154+
if (arg == null) {
155+
if (methodParam.getParameterAnnotation(RequestBody.class).required()) {
156+
throw new HttpMessageNotReadableException("Required request body is missing: " +
157+
methodParam.getMethod().toGenericString());
161158
}
162159
}
163160
return arg;

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,10 @@
1616

1717
package org.springframework.web.servlet.mvc.method.annotation;
1818

19-
import static org.junit.Assert.*;
20-
import static org.mockito.BDDMockito.*;
21-
import static org.springframework.web.servlet.HandlerMapping.*;
22-
2319
import java.lang.reflect.Method;
2420
import java.net.URI;
2521
import java.nio.charset.Charset;
2622
import java.text.SimpleDateFormat;
27-
import java.util.Arrays;
2823
import java.util.Collections;
2924
import java.util.Date;
3025
import java.util.Locale;
@@ -53,6 +48,10 @@
5348
import org.springframework.web.context.request.ServletWebRequest;
5449
import org.springframework.web.method.support.ModelAndViewContainer;
5550

51+
import static org.junit.Assert.*;
52+
import static org.mockito.BDDMockito.*;
53+
import static org.springframework.web.servlet.HandlerMapping.*;
54+
5655
/**
5756
* Test fixture for {@link HttpEntityMethodProcessor} delegating to a mock
5857
* {@link HttpMessageConverter}.
@@ -113,7 +112,7 @@ public void setUp() throws Exception {
113112
returnTypeInt = new MethodParameter(getClass().getMethod("handle3"), -1);
114113

115114
mavContainer = new ModelAndViewContainer();
116-
servletRequest = new MockHttpServletRequest("GET", "/foo");
115+
servletRequest = new MockHttpServletRequest("POST", "/foo");
117116
servletResponse = new MockHttpServletResponse();
118117
webRequest = new ServletWebRequest(servletRequest, servletResponse);
119118
}
@@ -185,7 +184,7 @@ public void resolveArgumentNotReadable() throws Exception {
185184
MediaType contentType = MediaType.TEXT_PLAIN;
186185
servletRequest.addHeader("Content-Type", contentType.toString());
187186

188-
given(messageConverter.getSupportedMediaTypes()).willReturn(Arrays.asList(contentType));
187+
given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(contentType));
189188
given(messageConverter.canRead(String.class, contentType)).willReturn(false);
190189

191190
processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
@@ -266,7 +265,7 @@ public void handleReturnValueNotAcceptable() throws Exception {
266265
servletRequest.addHeader("Accept", accepted.toString());
267266

268267
given(messageConverter.canWrite(String.class, null)).willReturn(true);
269-
given(messageConverter.getSupportedMediaTypes()).willReturn(Arrays.asList(MediaType.TEXT_PLAIN));
268+
given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
270269
given(messageConverter.canWrite(String.class, accepted)).willReturn(false);
271270

272271
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,15 @@
1616

1717
package org.springframework.web.servlet.mvc.method.annotation;
1818

19-
import com.fasterxml.jackson.annotation.JsonTypeInfo;
20-
import com.fasterxml.jackson.annotation.JsonTypeName;
21-
import static org.junit.Assert.*;
22-
2319
import java.io.Serializable;
2420
import java.lang.reflect.Method;
2521
import java.util.ArrayList;
2622
import java.util.Arrays;
23+
import java.util.Collections;
2724
import java.util.List;
2825

26+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
27+
import com.fasterxml.jackson.annotation.JsonTypeName;
2928
import org.junit.Before;
3029
import org.junit.Test;
3130

@@ -46,6 +45,8 @@
4645
import org.springframework.web.method.HandlerMethod;
4746
import org.springframework.web.method.support.ModelAndViewContainer;
4847

48+
import static org.junit.Assert.*;
49+
4950
/**
5051
* Test fixture with {@link HttpEntityMethodProcessor} delegating to
5152
* actual {@link HttpMessageConverter} instances.
@@ -54,6 +55,7 @@
5455
*
5556
* @author Rossen Stoyanchev
5657
*/
58+
@SuppressWarnings("unused")
5759
public class HttpEntityMethodProcessorTests {
5860

5961
private MethodParameter paramList;
@@ -81,7 +83,9 @@ public void setUp() throws Exception {
8183
binderFactory = new ValidatingBinderFactory();
8284
servletRequest = new MockHttpServletRequest();
8385
servletResponse = new MockHttpServletResponse();
86+
servletRequest.setMethod("POST");
8487
webRequest = new ServletWebRequest(servletRequest, servletResponse);
88+
8589
}
8690

8791
@Test
@@ -109,7 +113,7 @@ public void resolveArgumentWithEmptyBody() throws Exception {
109113
this.servletRequest.setContent(new byte[0]);
110114
this.servletRequest.setContentType("application/json");
111115

112-
List<HttpMessageConverter<?>> converters = Arrays.asList(new MappingJackson2HttpMessageConverter());
116+
List<HttpMessageConverter<?>> converters = Collections.singletonList(new MappingJackson2HttpMessageConverter());
113117
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
114118

115119
HttpEntity<?> result = (HttpEntity<?>) processor.resolveArgument(this.paramSimpleBean,
@@ -196,9 +200,9 @@ private static class MySimpleParameterizedController extends MyParameterizedCont
196200

197201
private interface Identifiable extends Serializable {
198202

199-
public Long getId();
203+
Long getId();
200204

201-
public void setId(Long id);
205+
void setId(Long id);
202206
}
203207

204208

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@
8080
import org.springframework.web.servlet.ModelAndView;
8181
import org.springframework.web.util.UriComponentsBuilder;
8282

83-
import static org.junit.Assert.*;
83+
import static org.junit.Assert.assertEquals;
84+
import static org.junit.Assert.assertFalse;
85+
import static org.junit.Assert.assertNotNull;
86+
import static org.junit.Assert.assertNull;
87+
import static org.junit.Assert.assertSame;
88+
import static org.junit.Assert.assertTrue;
8489

8590
/**
8691
* A test fixture with a controller with all supported method signature styles
@@ -102,6 +107,7 @@ public class RequestMappingHandlerAdapterIntegrationTests {
102107

103108
private MockHttpServletResponse response;
104109

110+
105111
@Before
106112
public void setup() throws Exception {
107113
ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer();
@@ -123,6 +129,8 @@ public void setup() throws Exception {
123129
request = new MockHttpServletRequest();
124130
response = new MockHttpServletResponse();
125131

132+
request.setMethod("POST");
133+
126134
// Expose request to the current thread (for SpEL expressions)
127135
RequestContextHolder.setRequestAttributes(new ServletWebRequest(request));
128136
}
@@ -132,6 +140,7 @@ public void teardown() {
132140
RequestContextHolder.resetRequestAttributes();
133141
}
134142

143+
135144
@Test
136145
public void handle() throws Exception {
137146

0 commit comments

Comments
 (0)