Improve empty request body handling
The check for an empty request body InputStream is now in the base class AbstractMessageConverterMethodArgumentResolver shared for all arguments that involve reading with an HttpMessageConverter -- @RequestBody, @RequestPart, and HttpEntity. When an empty body is detected any configured RequestBodyAdvice is given a chance to select a default value or leave it as null. Issue: SPR-12778, SPR-12860, SPR-12861
This commit is contained in:
@@ -16,14 +16,19 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.any;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.BDDMockito.isA;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.springframework.web.servlet.HandlerMapping.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -48,13 +53,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.any;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.BDDMockito.isA;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.springframework.web.servlet.HandlerMapping.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link HttpEntityMethodProcessor} delegating to a mock
|
||||
* {@link HttpMessageConverter}.
|
||||
@@ -136,10 +134,12 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
String body = "Foo";
|
||||
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setContent(body.getBytes(Charset.forName("UTF-8")));
|
||||
|
||||
String body = "Foo";
|
||||
given(messageConverter.canRead(String.class, contentType)).willReturn(true);
|
||||
given(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).willReturn(body);
|
||||
|
||||
@@ -152,14 +152,16 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
|
||||
@Test
|
||||
public void resolveArgumentRequestEntity() throws Exception {
|
||||
String body = "Foo";
|
||||
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setMethod("GET");
|
||||
servletRequest.setServerName("www.example.com");
|
||||
servletRequest.setServerPort(80);
|
||||
servletRequest.setRequestURI("/path");
|
||||
servletRequest.setContent(body.getBytes(Charset.forName("UTF-8")));
|
||||
|
||||
String body = "Foo";
|
||||
given(messageConverter.canRead(String.class, contentType)).willReturn(true);
|
||||
given(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).willReturn(body);
|
||||
|
||||
@@ -226,6 +228,7 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
verify(messageConverter).write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void handleReturnValueWithResponseBodyAdvice() throws Exception {
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<>(HttpStatus.OK);
|
||||
@@ -416,28 +419,35 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
assertEquals(0, servletResponse.getContentAsByteArray().length);
|
||||
}
|
||||
|
||||
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> responseEntity, int i, RequestEntity<String> requestEntity) {
|
||||
return responseEntity;
|
||||
@SuppressWarnings("unused")
|
||||
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> entity,
|
||||
int i, RequestEntity<String> requestEntity) {
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public HttpEntity<?> handle2(HttpEntity<?> entity) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public CustomHttpEntity handle2x(HttpEntity<?> entity) {
|
||||
return new CustomHttpEntity();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int handle3() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RequestMapping(produces = {"text/html", "application/xhtml+xml"})
|
||||
public ResponseEntity<String> handle4() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class CustomHttpEntity extends HttpEntity<Object> {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -39,8 +42,6 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link HttpEntityMethodProcessor} delegating to
|
||||
* actual {@link HttpMessageConverter} instances.
|
||||
@@ -61,8 +62,6 @@ public class HttpEntityMethodProcessorTests {
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
|
||||
@@ -75,8 +74,7 @@ public class HttpEntityMethodProcessorTests {
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
binderFactory = new ValidatingBinderFactory();
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,6 +95,23 @@ public class HttpEntityMethodProcessorTests {
|
||||
assertEquals("Jad", result.getBody().getName());
|
||||
}
|
||||
|
||||
// SPR-12861
|
||||
|
||||
@Test
|
||||
public void resolveArgumentWithEmptyBody() throws Exception {
|
||||
this.servletRequest.setContent(new byte[0]);
|
||||
this.servletRequest.setContentType("application/json");
|
||||
|
||||
List<HttpMessageConverter<?>> converters = Arrays.asList(new MappingJackson2HttpMessageConverter());
|
||||
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
|
||||
|
||||
HttpEntity<?> result = (HttpEntity<?>) processor.resolveArgument(this.paramSimpleBean,
|
||||
this.mavContainer, this.webRequest, this.binderFactory);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNull(result.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveGenericArgument() throws Exception {
|
||||
String content = "[{\"name\" : \"Jad\"}, {\"name\" : \"Robert\"}]";
|
||||
@@ -139,21 +154,21 @@ public class HttpEntityMethodProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle(HttpEntity<List<SimpleBean>> arg1, HttpEntity<SimpleBean> arg2) {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static abstract class MyParameterizedController<DTO extends Identifiable> {
|
||||
|
||||
public void handleDto(HttpEntity<DTO> dto) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MySimpleParameterizedController extends MyParameterizedController<SimpleBean> {
|
||||
}
|
||||
|
||||
|
||||
private interface Identifiable extends Serializable {
|
||||
|
||||
public Long getId();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
@@ -34,8 +39,10 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
@@ -57,8 +64,6 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test access to parts of a multipart request with {@link RequestPart}.
|
||||
*
|
||||
@@ -102,9 +107,16 @@ public class RequestPartIntegrationTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ByteArrayHttpMessageConverter emptyBodyConverter = new ByteArrayHttpMessageConverter();
|
||||
emptyBodyConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON));
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>(3);
|
||||
converters.add(emptyBodyConverter);
|
||||
converters.add(new ResourceHttpMessageConverter());
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
|
||||
AllEncompassingFormHttpMessageConverter converter = new AllEncompassingFormHttpMessageConverter();
|
||||
converter.setPartConverters(Arrays.<HttpMessageConverter<?>>asList(
|
||||
new ResourceHttpMessageConverter(), new MappingJackson2HttpMessageConverter()));
|
||||
converter.setPartConverters(converters);
|
||||
|
||||
restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
|
||||
restTemplate.setMessageConverters(Arrays.<HttpMessageConverter<?>>asList(converter));
|
||||
@@ -130,9 +142,9 @@ public class RequestPartIntegrationTests {
|
||||
|
||||
private void testCreate(String url) {
|
||||
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
|
||||
HttpEntity<TestData> jsonEntity = new HttpEntity<TestData>(new TestData("Jason"));
|
||||
parts.add("json-data", jsonEntity);
|
||||
parts.add("json-data", new HttpEntity<TestData>(new TestData("Jason")));
|
||||
parts.add("file-data", new ClassPathResource("logo.jpg", this.getClass()));
|
||||
parts.add("empty-data", new HttpEntity<byte[]>(new byte[0])); // SPR-12860
|
||||
|
||||
URI location = restTemplate.postForLocation(url, parts);
|
||||
assertEquals("http://localhost:8080/test/Jason/logo.jpg", location.toString());
|
||||
@@ -167,11 +179,15 @@ public class RequestPartIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Controller
|
||||
private static class RequestPartTestController {
|
||||
|
||||
@RequestMapping(value = "/test", method = RequestMethod.POST, consumes = { "multipart/mixed", "multipart/form-data" })
|
||||
public ResponseEntity<Object> create(@RequestPart("json-data") TestData testData, @RequestPart("file-data") MultipartFile file) {
|
||||
public ResponseEntity<Object> create(@RequestPart("json-data") TestData testData,
|
||||
@RequestPart("file-data") MultipartFile file,
|
||||
@RequestPart(value = "empty-data", required = false) TestData emptyData) {
|
||||
|
||||
String url = "http://localhost:8080/test/" + testData.getName() + "/" + file.getOriginalFilename();
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setLocation(URI.create(url));
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.servlet.http.Part;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
@@ -31,6 +36,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
@@ -52,9 +58,6 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link RequestPartMethodArgumentResolver} and mock {@link HttpMessageConverter}.
|
||||
*
|
||||
@@ -90,8 +93,6 @@ public class RequestPartMethodArgumentResolverTests {
|
||||
|
||||
private MockMultipartHttpServletRequest multipartRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
@@ -128,13 +129,14 @@ public class RequestPartMethodArgumentResolverTests {
|
||||
resolver = new RequestPartMethodArgumentResolver(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
|
||||
reset(messageConverter);
|
||||
|
||||
multipartFile1 = new MockMultipartFile("requestPart", "", "text/plain", (byte[]) null);
|
||||
multipartFile2 = new MockMultipartFile("requestPart", "", "text/plain", (byte[]) null);
|
||||
byte[] content = "doesn't matter as long as not empty".getBytes(Charset.forName("UTF-8"));
|
||||
|
||||
multipartFile1 = new MockMultipartFile("requestPart", "", "text/plain", content);
|
||||
multipartFile2 = new MockMultipartFile("requestPart", "", "text/plain", content);
|
||||
multipartRequest = new MockMultipartHttpServletRequest();
|
||||
multipartRequest.addFile(multipartFile1);
|
||||
multipartRequest.addFile(multipartFile2);
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(multipartRequest, servletResponse);
|
||||
webRequest = new ServletWebRequest(multipartRequest, new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
|
||||
@@ -361,7 +363,7 @@ public class RequestPartMethodArgumentResolverTests {
|
||||
SimpleBean simpleBean = new SimpleBean("foo");
|
||||
|
||||
given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true);
|
||||
given(messageConverter.read(eq(SimpleBean.class), isA(RequestPartServletServerHttpRequest.class))).willReturn(simpleBean);
|
||||
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(simpleBean);
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
@@ -385,7 +387,7 @@ public class RequestPartMethodArgumentResolverTests {
|
||||
|
||||
private void testResolveArgument(SimpleBean argValue, MethodParameter parameter) throws Exception {
|
||||
given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true);
|
||||
given(messageConverter.read(eq(SimpleBean.class), isA(RequestPartServletServerHttpRequest.class))).willReturn(argValue);
|
||||
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(argValue);
|
||||
|
||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||
Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
@@ -423,7 +425,7 @@ public class RequestPartMethodArgumentResolverTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle(@RequestPart SimpleBean requestPart,
|
||||
@RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart,
|
||||
@Valid @RequestPart("requestPart") SimpleBean validRequestPart,
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@@ -49,9 +52,6 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link RequestResponseBodyMethodProcessor} delegating to a
|
||||
* mock HttpMessageConverter.
|
||||
@@ -81,7 +81,6 @@ public class RequestResponseBodyMethodProcessorMockTests {
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
@@ -103,8 +102,7 @@ public class RequestResponseBodyMethodProcessorMockTests {
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,7 +152,7 @@ public class RequestResponseBodyMethodProcessorMockTests {
|
||||
testResolveArgumentWithValidation(new SimpleBean("name"));
|
||||
}
|
||||
|
||||
private void testResolveArgumentWithValidation(SimpleBean simpleBean) throws IOException, Exception {
|
||||
private void testResolveArgumentWithValidation(SimpleBean simpleBean) throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setContent("payload".getBytes(Charset.forName("UTF-8")));
|
||||
@@ -189,6 +187,7 @@ public class RequestResponseBodyMethodProcessorMockTests {
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (HttpMediaTypeNotSupportedException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +211,9 @@ public class RequestResponseBodyMethodProcessorMockTests {
|
||||
|
||||
@Test
|
||||
public void resolveArgumentNotRequiredNoContent() throws Exception {
|
||||
servletRequest.setContentType("text/plain");
|
||||
servletRequest.setContent(new byte[0]);
|
||||
given(messageConverter.canRead(String.class, MediaType.TEXT_PLAIN)).willReturn(true);
|
||||
assertNull(processor.resolveArgument(paramStringNotRequired, mavContainer, webRequest, new ValidatingBinderFactory()));
|
||||
}
|
||||
|
||||
@@ -293,23 +294,28 @@ public class RequestResponseBodyMethodProcessorMockTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ResponseBody
|
||||
public String handle1(@RequestBody String s, int i) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int handle2() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ResponseBody
|
||||
public String handle3() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle4(@Valid @RequestBody SimpleBean b) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void handle5(@RequestBody(required=false) String s) {
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -29,6 +30,7 @@ import org.junit.Test;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -177,7 +179,9 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
assertEquals("foobarbaz", result);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMessageNotReadableException.class) // SPR-9942
|
||||
// SPR-9942
|
||||
|
||||
@Test(expected = HttpMessageNotReadableException.class)
|
||||
public void resolveArgumentRequiredNoContent() throws Exception {
|
||||
this.servletRequest.setContent(new byte[0]);
|
||||
this.servletRequest.setContentType("text/plain");
|
||||
@@ -187,7 +191,23 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
processor.resolveArgument(paramString, mavContainer, webRequest, binderFactory);
|
||||
}
|
||||
|
||||
@Test // SPR-9964
|
||||
// SPR-12778
|
||||
|
||||
@Test
|
||||
public void resolveArgumentRequiredNoContentDefaultValue() throws Exception {
|
||||
this.servletRequest.setContent(new byte[0]);
|
||||
this.servletRequest.setContentType("text/plain");
|
||||
List<HttpMessageConverter<?>> converters = Arrays.asList(new StringHttpMessageConverter());
|
||||
List<Object> advice = Arrays.asList(new EmptyRequestBodyAdvice());
|
||||
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters, advice);
|
||||
String arg = (String) processor.resolveArgument(paramString, mavContainer, webRequest, binderFactory);
|
||||
assertNotNull(arg);
|
||||
assertEquals("default value for empty body", arg);
|
||||
}
|
||||
|
||||
// SPR-9964
|
||||
|
||||
@Test
|
||||
public void resolveArgumentTypeVariable() throws Exception {
|
||||
Method method = MyParameterizedController.class.getMethod("handleDto", Identifiable.class);
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedController(), method);
|
||||
@@ -207,7 +227,9 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
assertEquals("Jad", result.getName());
|
||||
}
|
||||
|
||||
@Test // SPR-11225
|
||||
// SPR-11225
|
||||
|
||||
@Test
|
||||
public void resolveArgumentTypeVariableWithNonGenericConverter() throws Exception {
|
||||
Method method = MyParameterizedController.class.getMethod("handleDto", Identifiable.class);
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedController(), method);
|
||||
@@ -229,7 +251,9 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
assertEquals("Jad", result.getName());
|
||||
}
|
||||
|
||||
@Test // SPR-9160
|
||||
// SPR-9160
|
||||
|
||||
@Test
|
||||
public void handleReturnValueSortByQuality() throws Exception {
|
||||
this.servletRequest.addHeader("Accept", "text/plain; q=0.5, application/json");
|
||||
|
||||
@@ -383,6 +407,7 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String handle(
|
||||
@RequestBody List<SimpleBean> list,
|
||||
@RequestBody SimpleBean simpleBean,
|
||||
@@ -393,6 +418,7 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static abstract class MyParameterizedController<DTO extends Identifiable> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -400,6 +426,7 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MySimpleParameterizedController extends MyParameterizedController<SimpleBean> {
|
||||
}
|
||||
|
||||
@@ -472,9 +499,10 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
private interface MyJacksonView1 {};
|
||||
private interface MyJacksonView2 {};
|
||||
private interface MyJacksonView1 {}
|
||||
private interface MyJacksonView2 {}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class JacksonViewBean {
|
||||
|
||||
@JsonView(MyJacksonView1.class)
|
||||
@@ -537,4 +565,35 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||
|
||||
}
|
||||
|
||||
private static class EmptyRequestBodyAdvice implements RequestBodyAdvice {
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
return StringHttpMessageConverter.class.equals(converterType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
return "default value for empty body";
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
return inputMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user