Support generic types in @RequestBody arguments
This change makes it possible to declare an @RequestBody argument with a generic type (e.g. List<Foo>). If a GenericHttpMessageConverter implementation supports the method argument, then the request will be converted to the apropiate target type. The new GenericHttpMessageConverter is implemented by the MappingJacksonHttpMessageConverter and also by a new Jaxb2CollectionHttpMessageConverter that can read read a generic Collection where the generic type is a JAXB type annotated with @XmlRootElement or @XmlType. Issue: SPR-9570
This commit is contained in:
parent
ed3823b045
commit
c9b7b132fb
|
|
@ -42,6 +42,8 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
|||
|
||||
private final Type responseType;
|
||||
|
||||
private final Class<T> responseClass;
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
private final Log logger;
|
||||
|
|
@ -64,10 +66,12 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
|||
this(responseType, messageConverters, LogFactory.getLog(HttpMessageConverterExtractor.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpMessageConverterExtractor(Type responseType, List<HttpMessageConverter<?>> messageConverters, Log logger) {
|
||||
Assert.notNull(responseType, "'responseType' must not be null");
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.responseType = responseType;
|
||||
this.responseClass = (responseType instanceof Class) ? (Class<T>) responseType : null;
|
||||
this.messageConverters = messageConverters;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
|
@ -79,21 +83,8 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
|||
}
|
||||
MediaType contentType = getContentType(response);
|
||||
|
||||
Class<T> responseClass = null;
|
||||
if (this.responseType instanceof Class) {
|
||||
responseClass = (Class) this.responseType;
|
||||
}
|
||||
for (HttpMessageConverter messageConverter : this.messageConverters) {
|
||||
if (responseClass != null) {
|
||||
if (messageConverter.canRead(responseClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + responseClass.getName() + "] as \"" +
|
||||
contentType + "\" using [" + messageConverter + "]");
|
||||
}
|
||||
return (T) messageConverter.read(responseClass, response);
|
||||
}
|
||||
}
|
||||
else if (messageConverter instanceof GenericHttpMessageConverter) {
|
||||
if (messageConverter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter genericMessageConverter = (GenericHttpMessageConverter) messageConverter;
|
||||
if (genericMessageConverter.canRead(this.responseType, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -103,6 +94,15 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
|||
return (T) genericMessageConverter.read(this.responseType, response);
|
||||
}
|
||||
}
|
||||
if (this.responseClass != null) {
|
||||
if (messageConverter.canRead(this.responseClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + this.responseClass.getName() + "] as \"" +
|
||||
contentType + "\" using [" + messageConverter + "]");
|
||||
}
|
||||
return (T) messageConverter.read(this.responseClass, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RestClientException(
|
||||
"Could not extract response: no suitable HttpMessageConverter found for response type [" +
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
|
|
@ -30,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -85,8 +87,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
|||
* @throws IOException if the reading from the request fails
|
||||
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
|
||||
*/
|
||||
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam, Class<T> paramType) throws IOException,
|
||||
HttpMediaTypeNotSupportedException {
|
||||
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
|
||||
MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
HttpInputMessage inputMessage = createInputMessage(webRequest);
|
||||
return readWithMessageConverters(inputMessage, methodParam, paramType);
|
||||
|
|
@ -106,20 +108,34 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter methodParam,
|
||||
Class<T> paramType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
Class<T> paramClass = (paramType instanceof Class) ? (Class) paramType : null;
|
||||
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (messageConverter.canRead(paramType, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
if (messageConverter instanceof GenericHttpMessageConverter) {
|
||||
GenericHttpMessageConverter genericMessageConverter = (GenericHttpMessageConverter) messageConverter;
|
||||
if (genericMessageConverter.canRead(paramType, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + paramType + "] as \"" +
|
||||
contentType + "\" using [" + messageConverter + "]");
|
||||
}
|
||||
return (T) genericMessageConverter.read(paramType, inputMessage);
|
||||
}
|
||||
}
|
||||
if (paramClass != null) {
|
||||
if (messageConverter.canRead(paramClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + paramClass.getName() + "] as \"" + contentType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
}
|
||||
return ((HttpMessageConverter<T>) messageConverter).read(paramClass, inputMessage);
|
||||
}
|
||||
return ((HttpMessageConverter<T>) messageConverter).read(paramType, inputMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,13 +79,13 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
HttpInputMessage inputMessage = createInputMessage(webRequest);
|
||||
Class<?> paramType = getHttpEntityType(parameter);
|
||||
Type paramType = getHttpEntityType(parameter);
|
||||
|
||||
Object body = readWithMessageConverters(webRequest, parameter, paramType);
|
||||
return new HttpEntity<Object>(body, inputMessage.getHeaders());
|
||||
}
|
||||
|
||||
private Class<?> getHttpEntityType(MethodParameter parameter) {
|
||||
private Type getHttpEntityType(MethodParameter parameter) {
|
||||
Assert.isAssignable(HttpEntity.class, parameter.getParameterType());
|
||||
ParameterizedType type = (ParameterizedType) parameter.getGenericParameterType();
|
||||
if (type.getActualTypeArguments().length == 1) {
|
||||
|
|
@ -97,10 +97,12 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
|
||||
if (componentType instanceof Class) {
|
||||
// Surely, there should be a nicer way to determine the array type
|
||||
Object array = Array.newInstance((Class<?>) componentType, 0);
|
||||
return array.getClass();
|
||||
return Array.newInstance((Class<?>) componentType, 0).getClass();
|
||||
}
|
||||
}
|
||||
else if (typeArgument instanceof ParameterizedType) {
|
||||
return typeArgument;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("HttpEntity parameter (" + parameter.getParameterName() + ") "
|
||||
+ "in method " + parameter.getMethod() + "is not parameterized");
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.Conventions;
|
||||
|
|
@ -86,7 +87,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
|||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
Object argument = readWithMessageConverters(webRequest, parameter, parameter.getParameterType());
|
||||
Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
|
||||
|
||||
String name = Conventions.getVariableNameForParameter(parameter);
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
|
||||
|
|
@ -134,7 +135,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
|||
|
||||
@Override
|
||||
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
|
||||
MethodParameter methodParam, Class<T> paramType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
if (inputMessage.getBody() != null) {
|
||||
return super.readWithMessageConverters(inputMessage, methodParam, paramType);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Copyright 2002-2012 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import static org.easymock.EasyMock.capture;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.eq;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.reset;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.web.servlet.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link HttpEntityMethodProcessor} delegating to a mock
|
||||
* {@link HttpMessageConverter}.
|
||||
*
|
||||
* <p>Also see {@link HttpEntityMethodProcessorTests}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HttpEntityMethodProcessorMockTests {
|
||||
|
||||
private HttpEntityMethodProcessor processor;
|
||||
|
||||
private HttpMessageConverter<String> messageConverter;
|
||||
|
||||
private MethodParameter paramHttpEntity;
|
||||
private MethodParameter paramResponseEntity;
|
||||
private MethodParameter paramInt;
|
||||
private MethodParameter returnTypeResponseEntity;
|
||||
private MethodParameter returnTypeHttpEntity;
|
||||
private MethodParameter returnTypeInt;
|
||||
private MethodParameter returnTypeResponseEntityProduces;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
private ServletWebRequest webRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messageConverter = createMock(HttpMessageConverter.class);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
replay(messageConverter);
|
||||
|
||||
processor = new HttpEntityMethodProcessor(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
|
||||
reset(messageConverter);
|
||||
|
||||
|
||||
Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE);
|
||||
paramHttpEntity = new MethodParameter(handle1, 0);
|
||||
paramResponseEntity = new MethodParameter(handle1, 1);
|
||||
paramInt = new MethodParameter(handle1, 2);
|
||||
returnTypeResponseEntity = new MethodParameter(handle1, -1);
|
||||
|
||||
returnTypeHttpEntity = new MethodParameter(getClass().getMethod("handle2", HttpEntity.class), -1);
|
||||
|
||||
returnTypeInt = new MethodParameter(getClass().getMethod("handle3"), -1);
|
||||
|
||||
returnTypeResponseEntityProduces = new MethodParameter(getClass().getMethod("handle4"), -1);
|
||||
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("HttpEntity parameter not supported", processor.supportsParameter(paramHttpEntity));
|
||||
assertFalse("ResponseEntity parameter supported", processor.supportsParameter(paramResponseEntity));
|
||||
assertFalse("non-entity parameter supported", processor.supportsParameter(paramInt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue("ResponseEntity return type not supported", processor.supportsReturnType(returnTypeResponseEntity));
|
||||
assertTrue("HttpEntity return type not supported", processor.supportsReturnType(returnTypeHttpEntity));
|
||||
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(returnTypeInt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
String body = "Foo";
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body);
|
||||
replay(messageConverter);
|
||||
|
||||
Object result = processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof HttpEntity);
|
||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||
assertEquals("Invalid argument", body, ((HttpEntity<?>) result).getBody());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNotReadable() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
|
||||
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNoContentType() throws Exception {
|
||||
processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(accepted), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
servletRequest.addHeader("Accept", "text/*");
|
||||
servletRequest.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
|
||||
|
||||
expect(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptable() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
MediaType accepted = MediaType.APPLICATION_ATOM_XML;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptableProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
|
||||
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
// SPR-9142
|
||||
|
||||
@Test(expected=HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptableParseError() throws Exception {
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>("Body", HttpStatus.ACCEPTED);
|
||||
servletRequest.addHeader("Accept", "01");
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseHeaderNoBody() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("headerName", "headerValue");
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(headers, HttpStatus.ACCEPTED);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
assertEquals("headerValue", servletResponse.getHeader("headerName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseHeaderAndBody() throws Exception {
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.set("header", "headerValue");
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>("body", responseHeaders, HttpStatus.ACCEPTED);
|
||||
|
||||
Capture<HttpOutputMessage> outputMessage = new Capture<HttpOutputMessage>();
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, MediaType.TEXT_PLAIN)).andReturn(true);
|
||||
messageConverter.write(eq("body"), eq(MediaType.TEXT_PLAIN), capture(outputMessage));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
assertEquals("headerValue", outputMessage.getValue().getHeaders().get("header").get(0));
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> responseEntity, int i) {
|
||||
return responseEntity;
|
||||
}
|
||||
|
||||
public HttpEntity<?> handle2(HttpEntity<?> entity) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public int handle3() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
@RequestMapping(produces = {"text/html", "application/xhtml+xml"})
|
||||
public ResponseEntity<String> handle4() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -15,64 +15,40 @@
|
|||
*/
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import static org.easymock.EasyMock.capture;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.eq;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.reset;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.web.servlet.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link HttpEntityMethodProcessor} and mock {@link HttpMessageConverter}.
|
||||
* Test fixture with {@link HttpEntityMethodProcessor} delegating to
|
||||
* actual {@link HttpMessageConverter} instances.
|
||||
*
|
||||
* <p>Also see {@link HttpEntityMethodProcessorMockTests}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HttpEntityMethodProcessorTests {
|
||||
|
||||
private HttpEntityMethodProcessor processor;
|
||||
|
||||
private HttpMessageConverter<String> messageConverter;
|
||||
|
||||
private MethodParameter paramHttpEntity;
|
||||
private MethodParameter paramResponseEntity;
|
||||
private MethodParameter paramInt;
|
||||
private MethodParameter returnTypeResponseEntity;
|
||||
private MethodParameter returnTypeHttpEntity;
|
||||
private MethodParameter returnTypeInt;
|
||||
private MethodParameter returnTypeResponseEntityProduces;
|
||||
private MethodParameter paramList;
|
||||
private MethodParameter paramSimpleBean;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
|
|
@ -82,28 +58,12 @@ public class HttpEntityMethodProcessorTests {
|
|||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messageConverter = createMock(HttpMessageConverter.class);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
replay(messageConverter);
|
||||
|
||||
processor = new HttpEntityMethodProcessor(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
|
||||
reset(messageConverter);
|
||||
|
||||
|
||||
Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE);
|
||||
paramHttpEntity = new MethodParameter(handle1, 0);
|
||||
paramResponseEntity = new MethodParameter(handle1, 1);
|
||||
paramInt = new MethodParameter(handle1, 2);
|
||||
returnTypeResponseEntity = new MethodParameter(handle1, -1);
|
||||
|
||||
returnTypeHttpEntity = new MethodParameter(getClass().getMethod("handle2", HttpEntity.class), -1);
|
||||
|
||||
returnTypeInt = new MethodParameter(getClass().getMethod("handle3"), -1);
|
||||
|
||||
returnTypeResponseEntityProduces = new MethodParameter(getClass().getMethod("handle4"), -1);
|
||||
Method method = getClass().getMethod("handle", HttpEntity.class, HttpEntity.class);
|
||||
paramList = new MethodParameter(method, 0);
|
||||
paramSimpleBean = new MethodParameter(method, 1);
|
||||
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
|
|
@ -112,191 +72,70 @@ public class HttpEntityMethodProcessorTests {
|
|||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("HttpEntity parameter not supported", processor.supportsParameter(paramHttpEntity));
|
||||
assertFalse("ResponseEntity parameter supported", processor.supportsParameter(paramResponseEntity));
|
||||
assertFalse("non-entity parameter supported", processor.supportsParameter(paramInt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue("ResponseEntity return type not supported", processor.supportsReturnType(returnTypeResponseEntity));
|
||||
assertTrue("HttpEntity return type not supported", processor.supportsReturnType(returnTypeHttpEntity));
|
||||
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(returnTypeInt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
String content = "{\"name\" : \"Jad\"}";
|
||||
this.servletRequest.setContent(content.getBytes("UTF-8"));
|
||||
this.servletRequest.setContentType("application/json");
|
||||
|
||||
String body = "Foo";
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body);
|
||||
replay(messageConverter);
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
|
||||
|
||||
Object result = processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpEntity<SimpleBean> result = (HttpEntity<SimpleBean>) processor.resolveArgument(
|
||||
paramSimpleBean, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
assertTrue(result instanceof HttpEntity);
|
||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||
assertEquals("Invalid argument", body, ((HttpEntity<?>) result).getBody());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNotReadable() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
|
||||
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNoContentType() throws Exception {
|
||||
processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
|
||||
fail("Expected exception");
|
||||
assertNotNull(result);
|
||||
assertEquals("Jad", result.getBody().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
public void resolveGenericArgument() throws Exception {
|
||||
String content = "[{\"name\" : \"Jad\"}, {\"name\" : \"Robert\"}]";
|
||||
this.servletRequest.setContent(content.getBytes("UTF-8"));
|
||||
this.servletRequest.setContentType("application/json");
|
||||
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters);
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(accepted), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpEntity<List<SimpleBean>> result = (HttpEntity<List<SimpleBean>>) processor.resolveArgument(
|
||||
paramList, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
assertNotNull(result);
|
||||
assertEquals("Jad", result.getBody().get(0).getName());
|
||||
assertEquals("Robert", result.getBody().get(1).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
servletRequest.addHeader("Accept", "text/*");
|
||||
servletRequest.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
|
||||
|
||||
expect(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
public void handle(HttpEntity<List<SimpleBean>> arg1, HttpEntity<SimpleBean> arg2) {
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptable() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
MediaType accepted = MediaType.APPLICATION_ATOM_XML;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
private static class SimpleBean {
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
private String name;
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
fail("Expected exception");
|
||||
@SuppressWarnings("unused")
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptableProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
|
||||
|
||||
fail("Expected exception");
|
||||
private final class ValidatingBinderFactory implements WebDataBinderFactory {
|
||||
public WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
|
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||
validator.afterPropertiesSet();
|
||||
WebDataBinder dataBinder = new WebDataBinder(target, objectName);
|
||||
dataBinder.setValidator(validator);
|
||||
return dataBinder;
|
||||
}
|
||||
}
|
||||
|
||||
// SPR-9142
|
||||
|
||||
@Test(expected=HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptableParseError() throws Exception {
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>("Body", HttpStatus.ACCEPTED);
|
||||
servletRequest.addHeader("Accept", "01");
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseHeaderNoBody() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("headerName", "headerValue");
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(headers, HttpStatus.ACCEPTED);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
assertEquals("headerValue", servletResponse.getHeader("headerName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseHeaderAndBody() throws Exception {
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.set("header", "headerValue");
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>("body", responseHeaders, HttpStatus.ACCEPTED);
|
||||
|
||||
Capture<HttpOutputMessage> outputMessage = new Capture<HttpOutputMessage>();
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, MediaType.TEXT_PLAIN)).andReturn(true);
|
||||
messageConverter.write(eq("body"), eq(MediaType.TEXT_PLAIN), capture(outputMessage));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
assertEquals("headerValue", outputMessage.getValue().getHeaders().get("header").get(0));
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> responseEntity, int i) {
|
||||
return responseEntity;
|
||||
}
|
||||
|
||||
public HttpEntity<?> handle2(HttpEntity<?> entity) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public int handle3() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
@RequestMapping(produces = {"text/html", "application/xhtml+xml"})
|
||||
public ResponseEntity<String> handle4() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* Copyright 2002-2012 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.eq;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.reset;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link RequestResponseBodyMethodProcessor} delegating to a
|
||||
* mock HttpMessageConverter.
|
||||
*
|
||||
* <p>Also see {@link RequestResponseBodyMethodProcessorTests}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestResponseBodyMethodProcessorMockTests {
|
||||
|
||||
private RequestResponseBodyMethodProcessor processor;
|
||||
|
||||
private HttpMessageConverter<String> messageConverter;
|
||||
|
||||
private MethodParameter paramRequestBodyString;
|
||||
private MethodParameter paramInt;
|
||||
private MethodParameter paramValidBean;
|
||||
private MethodParameter paramStringNotRequired;
|
||||
private MethodParameter returnTypeString;
|
||||
private MethodParameter returnTypeInt;
|
||||
private MethodParameter returnTypeStringProduces;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messageConverter = createMock(HttpMessageConverter.class);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
replay(messageConverter);
|
||||
|
||||
processor = new RequestResponseBodyMethodProcessor(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
|
||||
reset(messageConverter);
|
||||
|
||||
Method methodHandle1 = getClass().getMethod("handle1", String.class, Integer.TYPE);
|
||||
paramRequestBodyString = new MethodParameter(methodHandle1, 0);
|
||||
paramInt = new MethodParameter(methodHandle1, 1);
|
||||
returnTypeString = new MethodParameter(methodHandle1, -1);
|
||||
returnTypeInt = new MethodParameter(getClass().getMethod("handle2"), -1);
|
||||
returnTypeStringProduces = new MethodParameter(getClass().getMethod("handle3"), -1);
|
||||
paramValidBean = new MethodParameter(getClass().getMethod("handle4", SimpleBean.class), 0);
|
||||
paramStringNotRequired = new MethodParameter(getClass().getMethod("handle5", String.class), 0);
|
||||
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("RequestBody parameter not supported", processor.supportsParameter(paramRequestBodyString));
|
||||
assertFalse("non-RequestBody parameter supported", processor.supportsParameter(paramInt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue("ResponseBody return type not supported", processor.supportsReturnType(returnTypeString));
|
||||
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(returnTypeInt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
String body = "Foo";
|
||||
servletRequest.setContent(body.getBytes());
|
||||
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body);
|
||||
replay(messageConverter);
|
||||
|
||||
Object result = processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
assertEquals("Invalid argument", body, result);
|
||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentNotValid() throws Exception {
|
||||
try {
|
||||
testResolveArgumentWithValidation(new SimpleBean(null));
|
||||
fail("Expected exception");
|
||||
} catch (MethodArgumentNotValidException e) {
|
||||
assertEquals("simpleBean", e.getBindingResult().getObjectName());
|
||||
assertEquals(1, e.getBindingResult().getErrorCount());
|
||||
assertNotNull(e.getBindingResult().getFieldError("name"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentValid() throws Exception {
|
||||
testResolveArgumentWithValidation(new SimpleBean("name"));
|
||||
}
|
||||
|
||||
private void testResolveArgumentWithValidation(SimpleBean simpleBean) throws IOException, Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setContent(new byte[] {});
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpMessageConverter<SimpleBean> beanConverter = createMock(HttpMessageConverter.class);
|
||||
expect(beanConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(beanConverter.canRead(SimpleBean.class, contentType)).andReturn(true);
|
||||
expect(beanConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).andReturn(simpleBean);
|
||||
replay(beanConverter);
|
||||
|
||||
processor = new RequestResponseBodyMethodProcessor(Collections.<HttpMessageConverter<?>>singletonList(beanConverter));
|
||||
processor.resolveArgument(paramValidBean, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
verify(beanConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNotReadable() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setContent(new byte[] {});
|
||||
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNoContentType() throws Exception {
|
||||
servletRequest.setContent(new byte[] {});
|
||||
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMessageNotReadableException.class)
|
||||
public void resolveArgumentRequiredNoContent() throws Exception {
|
||||
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentNotRequiredNoContent() throws Exception {
|
||||
assertNull(processor.resolveArgument(paramStringNotRequired, mavContainer, webRequest, new ValidatingBinderFactory()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
String body = "Foo";
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(accepted), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(body, returnTypeString, mavContainer, webRequest);
|
||||
|
||||
assertTrue("The requestHandled flag wasn't set", mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
|
||||
servletRequest.addHeader("Accept", "text/*");
|
||||
servletRequest.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
|
||||
|
||||
expect(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(body, returnTypeStringProduces, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptable() throws Exception {
|
||||
MediaType accepted = MediaType.APPLICATION_ATOM_XML;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue("Foo", returnTypeString, mavContainer, webRequest);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptableProduces() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue("Foo", returnTypeStringProduces, mavContainer, webRequest);
|
||||
}
|
||||
|
||||
// SPR-9160
|
||||
|
||||
@Test
|
||||
public void handleReturnValueSortByQuality() throws Exception {
|
||||
this.servletRequest.addHeader("Accept", "text/plain; q=0.5, application/json");
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
converters.add(new StringHttpMessageConverter());
|
||||
RequestResponseBodyMethodProcessor handler = new RequestResponseBodyMethodProcessor(converters);
|
||||
|
||||
handler.writeWithMessageConverters("Foo", returnTypeStringProduces, webRequest);
|
||||
|
||||
assertEquals("application/json;charset=UTF-8", servletResponse.getHeader("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueString() throws Exception {
|
||||
List<HttpMessageConverter<?>>converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(new ByteArrayHttpMessageConverter());
|
||||
converters.add(new StringHttpMessageConverter());
|
||||
|
||||
processor = new RequestResponseBodyMethodProcessor(converters);
|
||||
processor.handleReturnValue("Foo", returnTypeString, mavContainer, webRequest);
|
||||
|
||||
assertEquals("text/plain;charset=ISO-8859-1", servletResponse.getHeader("Content-Type"));
|
||||
assertEquals("Foo", servletResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
public String handle1(@RequestBody String s, int i) {
|
||||
return s;
|
||||
}
|
||||
|
||||
public int handle2() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
public String handle3() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void handle4(@Valid @RequestBody SimpleBean b) {
|
||||
}
|
||||
|
||||
public void handle5(@RequestBody(required=false) String s) {
|
||||
}
|
||||
|
||||
private final class ValidatingBinderFactory implements WebDataBinderFactory {
|
||||
public WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
|
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||
validator.afterPropertiesSet();
|
||||
WebDataBinder dataBinder = new WebDataBinder(target, objectName);
|
||||
dataBinder.setValidator(validator);
|
||||
return dataBinder;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class SimpleBean {
|
||||
|
||||
@NotNull
|
||||
private final String name;
|
||||
|
||||
public SimpleBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,75 +16,43 @@
|
|||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.eq;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.reset;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link RequestResponseBodyMethodProcessor} and mock {@link HttpMessageConverter}.
|
||||
* Test fixture for a {@link RequestResponseBodyMethodProcessor} with actual delegation
|
||||
* to HttpMessageConverter instances.
|
||||
*
|
||||
* <p>Also see {@link RequestResponseBodyMethodProcessorMockTests}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestResponseBodyMethodProcessorTests {
|
||||
|
||||
private RequestResponseBodyMethodProcessor processor;
|
||||
|
||||
private HttpMessageConverter<String> messageConverter;
|
||||
|
||||
private MethodParameter paramRequestBodyString;
|
||||
private MethodParameter paramInt;
|
||||
private MethodParameter paramValidBean;
|
||||
private MethodParameter paramStringNotRequired;
|
||||
private MethodParameter paramGenericList;
|
||||
private MethodParameter paramSimpleBean;
|
||||
private MethodParameter returnTypeString;
|
||||
private MethodParameter returnTypeInt;
|
||||
private MethodParameter returnTypeStringProduces;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
|
|
@ -94,24 +62,13 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
|
||||
private MockHttpServletResponse servletResponse;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
messageConverter = createMock(HttpMessageConverter.class);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
replay(messageConverter);
|
||||
|
||||
processor = new RequestResponseBodyMethodProcessor(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
|
||||
reset(messageConverter);
|
||||
|
||||
Method handle = getClass().getMethod("handle1", String.class, Integer.TYPE);
|
||||
paramRequestBodyString = new MethodParameter(handle, 0);
|
||||
paramInt = new MethodParameter(handle, 1);
|
||||
returnTypeString = new MethodParameter(handle, -1);
|
||||
returnTypeInt = new MethodParameter(getClass().getMethod("handle2"), -1);
|
||||
returnTypeStringProduces = new MethodParameter(getClass().getMethod("handle3"), -1);
|
||||
paramValidBean = new MethodParameter(getClass().getMethod("handle4", SimpleBean.class), 0);
|
||||
paramStringNotRequired = new MethodParameter(getClass().getMethod("handle5", String.class), 0);
|
||||
Method method = getClass().getMethod("handle", List.class, SimpleBean.class);
|
||||
paramGenericList = new MethodParameter(method, 0);
|
||||
paramSimpleBean = new MethodParameter(method, 1);
|
||||
returnTypeString = new MethodParameter(method, -1);
|
||||
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
|
|
@ -120,160 +77,41 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue("RequestBody parameter not supported", processor.supportsParameter(paramRequestBodyString));
|
||||
assertFalse("non-RequestBody parameter supported", processor.supportsParameter(paramInt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() {
|
||||
assertTrue("ResponseBody return type not supported", processor.supportsReturnType(returnTypeString));
|
||||
assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(returnTypeInt));
|
||||
public void resolveGenericArgument() throws Exception {
|
||||
String content = "[{\"name\" : \"Jad\"}, {\"name\" : \"Robert\"}]";
|
||||
this.servletRequest.setContent(content.getBytes("UTF-8"));
|
||||
this.servletRequest.setContentType("application/json");
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SimpleBean> result = (List<SimpleBean>) processor.resolveArgument(
|
||||
paramGenericList, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals("Jad", result.get(0).getName());
|
||||
assertEquals("Robert", result.get(1).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
String content = "{\"name\" : \"Jad\"}";
|
||||
this.servletRequest.setContent(content.getBytes("UTF-8"));
|
||||
this.servletRequest.setContentType("application/json");
|
||||
|
||||
String body = "Foo";
|
||||
servletRequest.setContent(body.getBytes());
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
|
||||
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body);
|
||||
replay(messageConverter);
|
||||
SimpleBean result = (SimpleBean) processor.resolveArgument(
|
||||
paramSimpleBean, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
Object result = processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
assertEquals("Invalid argument", body, result);
|
||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentNotValid() throws Exception {
|
||||
try {
|
||||
testResolveArgumentWithValidation(new SimpleBean(null));
|
||||
fail("Expected exception");
|
||||
} catch (MethodArgumentNotValidException e) {
|
||||
assertEquals("simpleBean", e.getBindingResult().getObjectName());
|
||||
assertEquals(1, e.getBindingResult().getErrorCount());
|
||||
assertNotNull(e.getBindingResult().getFieldError("name"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentValid() throws Exception {
|
||||
testResolveArgumentWithValidation(new SimpleBean("name"));
|
||||
}
|
||||
|
||||
private void testResolveArgumentWithValidation(SimpleBean simpleBean) throws IOException, Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setContent(new byte[] {});
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
HttpMessageConverter<SimpleBean> beanConverter = createMock(HttpMessageConverter.class);
|
||||
expect(beanConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(beanConverter.canRead(SimpleBean.class, contentType)).andReturn(true);
|
||||
expect(beanConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).andReturn(simpleBean);
|
||||
replay(beanConverter);
|
||||
|
||||
processor = new RequestResponseBodyMethodProcessor(Collections.<HttpMessageConverter<?>>singletonList(beanConverter));
|
||||
processor.resolveArgument(paramValidBean, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||
|
||||
verify(beanConverter);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNotReadable() throws Exception {
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
servletRequest.setContent(new byte[] {});
|
||||
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotSupportedException.class)
|
||||
public void resolveArgumentNoContentType() throws Exception {
|
||||
servletRequest.setContent(new byte[] {});
|
||||
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMessageNotReadableException.class)
|
||||
public void resolveArgumentRequiredNoContent() throws Exception {
|
||||
processor.resolveArgument(paramRequestBodyString, mavContainer, webRequest, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentNotRequiredNoContent() throws Exception {
|
||||
assertNull(processor.resolveArgument(paramStringNotRequired, mavContainer, webRequest, new ValidatingBinderFactory()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValue() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
String body = "Foo";
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(accepted), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(body, returnTypeString, mavContainer, webRequest);
|
||||
|
||||
assertTrue("The requestHandled flag wasn't set", mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
|
||||
servletRequest.addHeader("Accept", "text/*");
|
||||
servletRequest.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
|
||||
|
||||
expect(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).andReturn(true);
|
||||
messageConverter.write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(body, returnTypeStringProduces, mavContainer, webRequest);
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
verify(messageConverter);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptable() throws Exception {
|
||||
MediaType accepted = MediaType.APPLICATION_ATOM_XML;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue("Foo", returnTypeString, mavContainer, webRequest);
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptableProduces() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue("Foo", returnTypeStringProduces, mavContainer, webRequest);
|
||||
assertNotNull(result);
|
||||
assertEquals("Jad", result.getName());
|
||||
}
|
||||
|
||||
// SPR-9160
|
||||
|
|
@ -285,9 +123,9 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(new MappingJackson2HttpMessageConverter());
|
||||
converters.add(new StringHttpMessageConverter());
|
||||
RequestResponseBodyMethodProcessor handler = new RequestResponseBodyMethodProcessor(converters);
|
||||
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
|
||||
|
||||
handler.writeWithMessageConverters("Foo", returnTypeStringProduces, webRequest);
|
||||
processor.writeWithMessageConverters("Foo", returnTypeString, webRequest);
|
||||
|
||||
assertEquals("application/json;charset=UTF-8", servletResponse.getHeader("Content-Type"));
|
||||
}
|
||||
|
|
@ -298,31 +136,31 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
converters.add(new ByteArrayHttpMessageConverter());
|
||||
converters.add(new StringHttpMessageConverter());
|
||||
|
||||
processor = new RequestResponseBodyMethodProcessor(converters);
|
||||
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters);
|
||||
processor.handleReturnValue("Foo", returnTypeString, mavContainer, webRequest);
|
||||
|
||||
assertEquals("text/plain;charset=ISO-8859-1", servletResponse.getHeader("Content-Type"));
|
||||
assertEquals("Foo", servletResponse.getContentAsString());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
public String handle1(@RequestBody String s, int i) {
|
||||
return s;
|
||||
}
|
||||
|
||||
public int handle2() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
public String handle3() {
|
||||
public String handle(@RequestBody List<SimpleBean> list, @RequestBody SimpleBean simpleBean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void handle4(@Valid @RequestBody SimpleBean b) {
|
||||
}
|
||||
|
||||
public void handle5(@RequestBody(required=false) String s) {
|
||||
private static class SimpleBean {
|
||||
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ValidatingBinderFactory implements WebDataBinderFactory {
|
||||
|
|
@ -335,19 +173,4 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class SimpleBean {
|
||||
|
||||
@NotNull
|
||||
private final String name;
|
||||
|
||||
public SimpleBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue