SPR-7353 - @ResponseBody and returned HttpEntity now respect @RequestMapping.produces()
This commit is contained in:
parent
57c757afc5
commit
ad2e0d4587
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
|
@ -575,7 +574,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
|||
/**
|
||||
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
|
||||
*/
|
||||
public static MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
|
||||
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
|
||||
|
||||
public boolean matches(Method method) {
|
||||
return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
|
||||
|
|
@ -585,7 +584,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
|||
/**
|
||||
* MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
|
||||
*/
|
||||
public static MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
|
||||
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
|
||||
|
||||
public boolean matches(Method method) {
|
||||
return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
|
@ -33,6 +34,9 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
|
@ -46,8 +50,8 @@ import org.springframework.web.servlet.handler.MappedInterceptors;
|
|||
import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
|
||||
|
||||
/**
|
||||
* An {@link AbstractHandlerMethodMapping} variant that uses {@link RequestMappingInfo}s for the registration and
|
||||
* the lookup of {@link HandlerMethod}s.
|
||||
* An {@link AbstractHandlerMethodMapping} variant that uses {@link RequestMappingInfo}s for the registration and the
|
||||
* lookup of {@link HandlerMethod}s.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
|
|
@ -86,8 +90,8 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* The handler determination in this method is made based on the presence of a type-level {@link Controller} annotation.
|
||||
* {@inheritDoc} The handler determination in this method is made based on the presence of a type-level {@link
|
||||
* Controller} annotation.
|
||||
*/
|
||||
@Override
|
||||
protected boolean isHandler(Class<?> beanType) {
|
||||
|
|
@ -106,9 +110,8 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link RequestMappingInfo} for the given method.
|
||||
* <p>Only {@link RequestMapping @RequestMapping}-annotated methods are considered.
|
||||
* Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their
|
||||
* Provides a {@link RequestMappingInfo} for the given method. <p>Only {@link RequestMapping @RequestMapping}-annotated
|
||||
* methods are considered. Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their
|
||||
* attributes combined with method-level {@link RequestMapping @RequestMapping} attributes.
|
||||
*
|
||||
* @param method the method to create a mapping for
|
||||
|
|
@ -137,12 +140,11 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
|||
|
||||
private static RequestMappingInfo createFromRequestMapping(RequestMapping annotation) {
|
||||
return new RequestMappingInfo(Arrays.asList(annotation.value()),
|
||||
RequestConditionFactory.parseMethods(annotation.method()),
|
||||
RequestConditionFactory.parseParams(annotation.params()),
|
||||
RequestConditionFactory.parseHeaders(annotation.headers()),
|
||||
RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()),
|
||||
RequestConditionFactory.parseProduces(annotation.produces(), annotation.headers())
|
||||
);
|
||||
RequestConditionFactory.parseMethods(annotation.method()),
|
||||
RequestConditionFactory.parseParams(annotation.params()),
|
||||
RequestConditionFactory.parseHeaders(annotation.headers()),
|
||||
RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()),
|
||||
RequestConditionFactory.parseProduces(annotation.produces(), annotation.headers()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -152,10 +154,13 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
|||
|
||||
/**
|
||||
* Returns a new {@link RequestMappingInfo} with attributes matching to the current request or {@code null}.
|
||||
*
|
||||
* @see RequestMappingInfo#getMatchingRequestMapping(String, HttpServletRequest, PathMatcher)
|
||||
*/
|
||||
@Override
|
||||
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo mapping, String lookupPath, HttpServletRequest request) {
|
||||
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo mapping,
|
||||
String lookupPath,
|
||||
HttpServletRequest request) {
|
||||
return mapping.getMatchingRequestMapping(lookupPath, request, pathMatcher);
|
||||
}
|
||||
|
||||
|
|
@ -177,26 +182,43 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
|||
|
||||
/**
|
||||
* Iterates all {@link RequestMappingInfo}s looking for mappings that match by URL but not by HTTP method.
|
||||
* @exception HttpRequestMethodNotSupportedException if there are matches by URL but not by HTTP method
|
||||
*
|
||||
* @throws HttpRequestMethodNotSupportedException if there are matches by URL but not by HTTP method
|
||||
*/
|
||||
@Override
|
||||
protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos, String lookupPath, HttpServletRequest request)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos,
|
||||
String lookupPath,
|
||||
HttpServletRequest request) throws ServletException {
|
||||
Set<String> allowedMethods = new HashSet<String>(6);
|
||||
Set<MediaType> consumableMediaTypes = new HashSet<MediaType>();
|
||||
Set<MediaType> producibleMediaTypes = new HashSet<MediaType>();
|
||||
for (RequestMappingInfo info : requestMappingInfos) {
|
||||
for (String pattern : info.getPatterns()) {
|
||||
if (pathMatcher.match(pattern, lookupPath)) {
|
||||
for (RequestMethod method : info.getMethods().getMethods()) {
|
||||
allowedMethods.add(method.name());
|
||||
}
|
||||
if (!info.getMethods().match(request)) {
|
||||
for (RequestMethod method : info.getMethods().getMethods()) {
|
||||
allowedMethods.add(method.name());
|
||||
}
|
||||
}
|
||||
if (!info.getConsumes().match(request)) {
|
||||
consumableMediaTypes.addAll(info.getConsumes().getMediaTypes());
|
||||
}
|
||||
if (!info.getProduces().match(request)) {
|
||||
producibleMediaTypes.addAll(info.getProduces().getMediaTypes());
|
||||
}
|
||||
}
|
||||
if (!allowedMethods.isEmpty()) {
|
||||
throw new HttpRequestMethodNotSupportedException(request.getMethod(),
|
||||
allowedMethods.toArray(new String[allowedMethods.size()]));
|
||||
|
||||
} else {
|
||||
throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
|
||||
}
|
||||
else if (!consumableMediaTypes.isEmpty()) {
|
||||
MediaType contentType = null;
|
||||
if (StringUtils.hasLength(request.getContentType())) {
|
||||
contentType = MediaType.parseMediaType(request.getContentType());
|
||||
}
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
|
||||
}
|
||||
else if (!producibleMediaTypes.isEmpty()) {
|
||||
throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -218,13 +240,13 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
|||
}
|
||||
|
||||
/**
|
||||
* A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context of a
|
||||
* specific request. For example not all {@link RequestMappingInfo} patterns may apply to the current request.
|
||||
* Therefore an HttpServletRequest is required as input.
|
||||
* A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context of a specific
|
||||
* request. For example not all {@link RequestMappingInfo} patterns may apply to the current request. Therefore an
|
||||
* HttpServletRequest is required as input.
|
||||
*
|
||||
* <p>Furthermore, the following assumptions are made about the input RequestMappings:
|
||||
* <ul><li>Each RequestMappingInfo has been fully matched to the request <li>The RequestMappingInfo contains
|
||||
* matched patterns only <li>Patterns are ordered with the best matching pattern at the top </ul>
|
||||
* <p>Furthermore, the following assumptions are made about the input RequestMappings: <ul><li>Each RequestMappingInfo
|
||||
* has been fully matched to the request <li>The RequestMappingInfo contains matched patterns only <li>Patterns are
|
||||
* ordered with the best matching pattern at the top </ul>
|
||||
*
|
||||
* @see RequestMappingHandlerMapping#getMatchingMapping(RequestMappingInfo, String, HttpServletRequest)
|
||||
*/
|
||||
|
|
@ -289,26 +311,6 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
|||
}
|
||||
}
|
||||
|
||||
private int compareAcceptHeaders(List<MediaType> accept, List<MediaType> otherAccept) {
|
||||
for (MediaType requestAccept : this.requestAcceptHeader) {
|
||||
int pos1 = indexOfIncluded(requestAccept, accept);
|
||||
int pos2 = indexOfIncluded(requestAccept, otherAccept);
|
||||
if (pos1 != pos2) {
|
||||
return pos2 - pos1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int indexOfIncluded(MediaType requestAccept, List<MediaType> accept) {
|
||||
for (int i = 0; i < accept.size(); i++) {
|
||||
if (requestAccept.includes(accept.get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,29 +17,37 @@
|
|||
package org.springframework.web.servlet.mvc.method.annotation.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
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.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
|
||||
/**
|
||||
* A base class for resolving method argument values by reading from the body of a request with
|
||||
* {@link HttpMessageConverter}s and for handling method return values by writing to the response with
|
||||
* {@link HttpMessageConverter}s.
|
||||
* A base class for resolving method argument values by reading from the body of a request with {@link
|
||||
* HttpMessageConverter}s and for handling method return values by writing to the response with {@link
|
||||
* HttpMessageConverter}s.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
|
|
@ -48,13 +56,28 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
|||
public abstract class AbstractMessageConverterMethodProcessor
|
||||
implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
private final List<MediaType> allSupportedMediaTypes;
|
||||
|
||||
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Assert.notNull(messageConverters, "'messageConverters' must not be null");
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.messageConverters = messageConverters;
|
||||
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
|
||||
}
|
||||
|
||||
private static List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Set<MediaType> allSupportedMediaTypes = new HashSet<MediaType>();
|
||||
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||
}
|
||||
List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes);
|
||||
MediaType.sortBySpecificity(result);
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -67,84 +90,127 @@ public abstract class AbstractMessageConverterMethodProcessor
|
|||
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
|
||||
String paramName = methodParam.getParameterName();
|
||||
if (paramName != null) {
|
||||
builder.append(' ');
|
||||
builder.append(paramName);
|
||||
}
|
||||
throw new HttpMediaTypeNotSupportedException("Cannot read parameter (" + builder.toString() +
|
||||
") using HttpMessageConverters: no Content-Type found in HTTP request");
|
||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||
if (this.messageConverters != null) {
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||
if (messageConverter.canRead(paramType, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
}
|
||||
return ((HttpMessageConverter<T>) messageConverter).read(paramType, inputMessage);
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (messageConverter.canRead(paramType, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
}
|
||||
return ((HttpMessageConverter<T>) messageConverter).read(paramType, inputMessage);
|
||||
}
|
||||
}
|
||||
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
|
||||
}
|
||||
|
||||
protected abstract HttpInputMessage createInputMessage(NativeWebRequest webRequest);
|
||||
|
||||
protected void writeWithMessageConverters(NativeWebRequest webRequest, Object returnValue)
|
||||
throws IOException, HttpMediaTypeNotAcceptableException {
|
||||
writeWithMessageConverters(returnValue, createInputMessage(webRequest), createOutputMessage(webRequest));
|
||||
/**
|
||||
* Creates a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
|
||||
*
|
||||
* @param webRequest the web request to create an input message from
|
||||
* @return the input message
|
||||
*/
|
||||
protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
return new ServletServerHttpRequest(servletRequest);
|
||||
}
|
||||
|
||||
protected abstract HttpOutputMessage createOutputMessage(NativeWebRequest webRequest);
|
||||
/**
|
||||
* Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}.
|
||||
*
|
||||
* @param webRequest the web request to create an output message from
|
||||
* @return the output message
|
||||
*/
|
||||
protected HttpOutputMessage createOutputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
|
||||
return new ServletServerHttpResponse(servletResponse);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> void writeWithMessageConverters(T returnValue,
|
||||
MethodParameter returnType,
|
||||
HttpInputMessage inputMessage,
|
||||
HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMediaTypeNotAcceptableException {
|
||||
|
||||
List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(inputMessage);
|
||||
Set<MediaType> producibleMediaTypes = getProducibleMediaTypes(returnType.getMethod(), returnValue.getClass());
|
||||
Set<MediaType> acceptableMediaTypes = getAcceptableMediaTypes(inputMessage);
|
||||
|
||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||
if (this.messageConverters != null) {
|
||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (!messageConverter.canWrite(returnValue.getClass(), acceptedMediaType)) {
|
||||
continue;
|
||||
}
|
||||
((HttpMessageConverter<T>) messageConverter).write(returnValue, acceptedMediaType, outputMessage);
|
||||
List<MediaType> mediaTypes = new ArrayList<MediaType>();
|
||||
for (MediaType acceptableMediaType : acceptableMediaTypes) {
|
||||
for (MediaType producibleMediaType : producibleMediaTypes) {
|
||||
if (acceptableMediaType.isCompatibleWith(producibleMediaType)) {
|
||||
mediaTypes.add(getMostSpecificMediaType(acceptableMediaType, producibleMediaType));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mediaTypes.isEmpty()) {
|
||||
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
|
||||
}
|
||||
MediaType.sortBySpecificity(mediaTypes);
|
||||
MediaType selectedMediaType = null;
|
||||
for (MediaType mediaType : mediaTypes) {
|
||||
if (mediaType.isConcrete()) {
|
||||
selectedMediaType = mediaType;
|
||||
break;
|
||||
}
|
||||
else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
|
||||
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
}
|
||||
if (selectedMediaType != null) {
|
||||
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||
if (messageConverter.canWrite(returnValue.getClass(), selectedMediaType)) {
|
||||
((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||
if (contentType == null) {
|
||||
contentType = acceptedMediaType;
|
||||
}
|
||||
logger.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" +
|
||||
logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
|
||||
messageConverter + "]");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||
}
|
||||
}
|
||||
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
|
||||
else {
|
||||
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
|
||||
}
|
||||
}
|
||||
|
||||
private List<MediaType> getAcceptedMediaTypes(HttpInputMessage inputMessage) {
|
||||
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
||||
if (acceptedMediaTypes.isEmpty()) {
|
||||
acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
|
||||
private Set<MediaType> getProducibleMediaTypes(Method handlerMethod, Class<?> returnValueClass) {
|
||||
RequestMapping requestMappingAnn = handlerMethod.getAnnotation(RequestMapping.class);
|
||||
if (requestMappingAnn == null) {
|
||||
requestMappingAnn = handlerMethod.getClass().getAnnotation(RequestMapping.class);
|
||||
}
|
||||
Set<MediaType> result = new HashSet<MediaType>();
|
||||
if (requestMappingAnn != null) {
|
||||
for (String produce : requestMappingAnn.produces()) {
|
||||
result.add(MediaType.parseMediaType(produce));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (HttpMessageConverter<?> messageConverter : messageConverters) {
|
||||
if (messageConverter.canWrite(returnValueClass, null)) {
|
||||
result.addAll(messageConverter.getSupportedMediaTypes());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
result.add(MediaType.ALL);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MediaType.sortByQualityValue(acceptedMediaTypes);
|
||||
return acceptedMediaTypes;
|
||||
private Set<MediaType> getAcceptableMediaTypes(HttpInputMessage inputMessage) {
|
||||
Set<MediaType> result = new HashSet<MediaType>(inputMessage.getHeaders().getAccept());
|
||||
if (result.isEmpty()) {
|
||||
result.add(MediaType.ALL);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private MediaType getMostSpecificMediaType(MediaType type1, MediaType type2) {
|
||||
return MediaType.SPECIFICITY_COMPARATOR.compare(type1, type2) < 0 ? type1 : type2;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,9 +23,6 @@ import java.lang.reflect.ParameterizedType;
|
|||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
|
@ -34,8 +31,6 @@ import org.springframework.http.HttpOutputMessage;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
|
|
@ -71,8 +66,10 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory)
|
||||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
HttpInputMessage inputMessage = createInputMessage(webRequest);
|
||||
Class<?> paramType = getHttpEntityType(parameter);
|
||||
|
||||
Object body = readWithMessageConverters(webRequest, parameter, paramType);
|
||||
return new HttpEntity<Object>(body, inputMessage.getHeaders());
|
||||
}
|
||||
|
|
@ -88,7 +85,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
else if (typeArgument instanceof GenericArrayType) {
|
||||
Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
|
||||
if (componentType instanceof Class) {
|
||||
// Surely, there should be a nicer way to do this
|
||||
// Surely, there should be a nicer way to determine the array type
|
||||
Object array = Array.newInstance((Class<?>) componentType, 0);
|
||||
return array.getClass();
|
||||
}
|
||||
|
|
@ -98,16 +95,11 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
"HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
return new ServletServerHttpRequest(servletRequest);
|
||||
}
|
||||
|
||||
public void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
mavContainer.setResolveView(false);
|
||||
|
||||
if (returnValue == null) {
|
||||
|
|
@ -129,7 +121,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
|
||||
Object body = responseEntity.getBody();
|
||||
if (body != null) {
|
||||
writeWithMessageConverters(body, createInputMessage(webRequest), outputMessage);
|
||||
writeWithMessageConverters(body, returnType, createInputMessage(webRequest), outputMessage);
|
||||
}
|
||||
else {
|
||||
// flush headers to the HttpServletResponse
|
||||
|
|
@ -137,10 +129,4 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpOutputMessage createOutputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
|
||||
return new ServletServerHttpResponse(servletResponse);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,15 +18,11 @@ package org.springframework.web.servlet.mvc.method.annotation.support;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
|
|
@ -37,8 +33,8 @@ import org.springframework.web.context.request.NativeWebRequest;
|
|||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves method arguments annotated with @{@link RequestBody}.
|
||||
* Handles return values from methods annotated with @{@link ResponseBody}.
|
||||
* Resolves method arguments annotated with @{@link RequestBody}. Handles return values from methods annotated with
|
||||
* {@link ResponseBody}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
|
|
@ -66,19 +62,14 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
|||
return readWithMessageConverters(webRequest, parameter, parameter.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpInputMessage createInputMessage(NativeWebRequest webRequest) {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
return new ServletServerHttpRequest(servletRequest);
|
||||
}
|
||||
|
||||
public void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
|
||||
mavContainer.setResolveView(false);
|
||||
if (returnValue != null) {
|
||||
writeWithMessageConverters(webRequest, returnValue);
|
||||
writeWithMessageConverters(returnValue, returnType, createInputMessage(webRequest),
|
||||
createOutputMessage(webRequest));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ package org.springframework.web.servlet.mvc.method.condition;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
|
@ -54,6 +56,18 @@ class MediaTypesRequestCondition<T extends MediaTypesRequestCondition.MediaTypeR
|
|||
return sortedConditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link MediaType}s contained in this condition.
|
||||
*/
|
||||
public Set<MediaType> getMediaTypes() {
|
||||
Set<MediaType> result = new LinkedHashSet<MediaType>();
|
||||
for (MediaTypeRequestCondition condition : getConditions()) {
|
||||
result.add(condition.getMediaType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -806,13 +806,10 @@ public class ServletHandlerMethodTests {
|
|||
|
||||
@Test
|
||||
public void responseBodyNoAcceptableMediaType() throws ServletException, IOException {
|
||||
initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() {
|
||||
initDispatcherServlet(RequestResponseBodyProducesController.class, new BeanDefinitionRegistrar() {
|
||||
public void register(GenericWebApplicationContext wac) {
|
||||
RootBeanDefinition converterDef = new RootBeanDefinition(StringHttpMessageConverter.class);
|
||||
converterDef.getPropertyValues().add("supportedMediaTypes", new MediaType("text", "plain"));
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
|
||||
StringHttpMessageConverter converter = new StringHttpMessageConverter();
|
||||
converter.setSupportedMediaTypes(Collections.singletonList(new MediaType("text", "plain")));
|
||||
adapterDef.getPropertyValues().add("messageConverters", converter);
|
||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||
}
|
||||
|
|
@ -923,7 +920,7 @@ public class ServletHandlerMethodTests {
|
|||
* See SPR-6877
|
||||
*/
|
||||
@Test
|
||||
public void overlappingMesssageConvertersRequestBody() throws ServletException, IOException {
|
||||
public void overlappingMessageConvertersRequestBody() throws ServletException, IOException {
|
||||
initDispatcherServlet(RequestResponseBodyController.class, new BeanDefinitionRegistrar() {
|
||||
public void register(GenericWebApplicationContext wac) {
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
|
||||
|
|
@ -942,7 +939,7 @@ public class ServletHandlerMethodTests {
|
|||
request.addHeader("Accept", "application/json, text/javascript, */*");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("Invalid response status code", "application/json", response.getHeader("Content-Type"));
|
||||
assertEquals("Invalid content-type", "application/json", response.getHeader("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1006,7 +1003,7 @@ public class ServletHandlerMethodTests {
|
|||
request.setContentType("application/xml");
|
||||
response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals(404, response.getStatus());
|
||||
assertEquals(415, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1053,6 +1050,12 @@ public class ServletHandlerMethodTests {
|
|||
response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals("xml", response.getContentAsString());
|
||||
|
||||
request = new MockHttpServletRequest("GET", "/something");
|
||||
request.addHeader("Accept", "application/msword");
|
||||
response = new MockHttpServletResponse();
|
||||
servlet.service(request, response);
|
||||
assertEquals(406, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -2159,6 +2162,16 @@ public class ServletHandlerMethodTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public static class RequestResponseBodyProducesController {
|
||||
|
||||
@RequestMapping(value = "/something", method = RequestMethod.PUT, produces = "text/plain")
|
||||
@ResponseBody
|
||||
public String handle(@RequestBody String body) throws IOException {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public static class ResponseBodyVoidController {
|
||||
|
||||
|
|
@ -2608,7 +2621,7 @@ public class ServletHandlerMethodTests {
|
|||
}
|
||||
|
||||
private interface BeanDefinitionRegistrar {
|
||||
public void register(GenericWebApplicationContext context);
|
||||
void register(GenericWebApplicationContext context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
|
|
|
|||
|
|
@ -16,26 +16,14 @@
|
|||
|
||||
package org.springframework.web.servlet.mvc.method.annotation.support;
|
||||
|
||||
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.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 java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
|
@ -49,9 +37,13 @@ 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 static org.easymock.EasyMock.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link HttpEntityMethodProcessor} and mock {@link HttpMessageConverter}.
|
||||
*
|
||||
|
|
@ -70,6 +62,7 @@ public class HttpEntityMethodProcessorTests {
|
|||
private MethodParameter returnTypeResponseEntity;
|
||||
private MethodParameter returnTypeHttpEntity;
|
||||
private MethodParameter returnTypeInt;
|
||||
private MethodParameter returnTypeResponseEntityProduces;
|
||||
|
||||
private ModelAndViewContainer mavContainer;
|
||||
|
||||
|
|
@ -83,10 +76,12 @@ public class HttpEntityMethodProcessorTests {
|
|||
@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);
|
||||
|
||||
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
messageConverters.add(messageConverter);
|
||||
processor = new HttpEntityMethodProcessor(messageConverters);
|
||||
|
||||
Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE);
|
||||
paramHttpEntity = new MethodParameter(handle1, 0);
|
||||
|
|
@ -95,8 +90,11 @@ public class HttpEntityMethodProcessorTests {
|
|||
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();
|
||||
|
|
@ -124,7 +122,6 @@ public class HttpEntityMethodProcessorTests {
|
|||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
String body = "Foo";
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body);
|
||||
replay(messageConverter);
|
||||
|
|
@ -165,6 +162,8 @@ public class HttpEntityMethodProcessorTests {
|
|||
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);
|
||||
|
|
@ -175,19 +174,53 @@ public class HttpEntityMethodProcessorTests {
|
|||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
|
||||
|
||||
servletRequest.addHeader("Accept", "text/*");
|
||||
|
||||
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);
|
||||
|
||||
assertFalse(mavContainer.isResolveView());
|
||||
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, accepted)).andReturn(false);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
|
||||
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
|
||||
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
|
@ -211,8 +244,10 @@ public class HttpEntityMethodProcessorTests {
|
|||
ResponseEntity<String> returnValue = new ResponseEntity<String>("body", responseHeaders, HttpStatus.ACCEPTED);
|
||||
|
||||
Capture<HttpOutputMessage> outputMessage = new Capture<HttpOutputMessage>();
|
||||
expect(messageConverter.canWrite(String.class, MediaType.ALL)).andReturn(true);
|
||||
messageConverter.write(eq("body"), eq(MediaType.ALL), capture(outputMessage));
|
||||
expect(messageConverter.canWrite(String.class, null)).andReturn(true);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(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);
|
||||
|
|
@ -234,4 +269,10 @@ public class HttpEntityMethodProcessorTests {
|
|||
return 42;
|
||||
}
|
||||
|
||||
@RequestMapping(produces = {"text/html", "application/xhtml+xml"})
|
||||
public ResponseEntity<String> handle4() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -16,24 +16,13 @@
|
|||
|
||||
package org.springframework.web.servlet.mvc.method.annotation.support;
|
||||
|
||||
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.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 java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
|
|
@ -44,11 +33,15 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
|||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import static org.easymock.EasyMock.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link RequestResponseBodyMethodProcessor} and mock {@link HttpMessageConverter}.
|
||||
*
|
||||
|
|
@ -66,31 +59,40 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
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);
|
||||
|
||||
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
messageConverters.add(messageConverter);
|
||||
processor = new RequestResponseBodyMethodProcessor(messageConverters);
|
||||
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);
|
||||
|
||||
mavContainer = new ModelAndViewContainer();
|
||||
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
|
||||
servletResponse = new MockHttpServletResponse();
|
||||
webRequest = new ServletWebRequest(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -111,7 +113,6 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
servletRequest.addHeader("Content-Type", contentType.toString());
|
||||
|
||||
String body = "Foo";
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(contentType));
|
||||
expect(messageConverter.canRead(String.class, contentType)).andReturn(true);
|
||||
expect(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).andReturn(body);
|
||||
|
||||
|
|
@ -150,6 +151,8 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
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);
|
||||
|
|
@ -160,16 +163,47 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
verify(messageConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleReturnValueProduces() throws Exception {
|
||||
String body = "Foo";
|
||||
|
||||
servletRequest.addHeader("Accept", "text/*");
|
||||
|
||||
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);
|
||||
|
||||
assertFalse(mavContainer.isResolveView());
|
||||
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);
|
||||
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
||||
@Test(expected = HttpMediaTypeNotAcceptableException.class)
|
||||
public void handleReturnValueNotAcceptableProduces() throws Exception {
|
||||
MediaType accepted = MediaType.TEXT_PLAIN;
|
||||
servletRequest.addHeader("Accept", accepted.toString());
|
||||
|
||||
expect(messageConverter.canWrite(String.class, accepted)).andReturn(false);
|
||||
expect(messageConverter.getSupportedMediaTypes()).andReturn(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
|
||||
replay(messageConverter);
|
||||
|
||||
processor.handleReturnValue("Foo", returnTypeString, mavContainer, webRequest);
|
||||
processor.handleReturnValue("Foo", returnTypeStringProduces, mavContainer, webRequest);
|
||||
|
||||
fail("Expected exception");
|
||||
}
|
||||
|
|
@ -183,4 +217,10 @@ public class RequestResponseBodyMethodProcessorTests {
|
|||
return 42;
|
||||
}
|
||||
|
||||
@RequestMapping(produces = {"text/html", "application/xhtml+xml"})
|
||||
@ResponseBody
|
||||
public String handle3() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -322,7 +322,7 @@ public class MediaType implements Comparable<MediaType> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the {@linkplain #getType() type} is the wildcard character <code>*</code> or not.
|
||||
* Indicates whether the {@linkplain #getType() type} is the wildcard character <code>*</code> or not.
|
||||
*/
|
||||
public boolean isWildcardType() {
|
||||
return WILDCARD_TYPE.equals(type);
|
||||
|
|
@ -336,13 +336,22 @@ public class MediaType implements Comparable<MediaType> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the {@linkplain #getSubtype() subtype} is the wildcard character <code>*</code> or not.
|
||||
* Indicates whether the {@linkplain #getSubtype() subtype} is the wildcard character <code>*</code> or not.
|
||||
* @return whether the subtype is <code>*</code>
|
||||
*/
|
||||
public boolean isWildcardSubtype() {
|
||||
return WILDCARD_TYPE.equals(subtype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this media type is concrete, i.e. whether neither the type or subtype is a wildcard
|
||||
* character <code>*</code>.
|
||||
* @return whether this media type is concrete
|
||||
*/
|
||||
public boolean isConcrete() {
|
||||
return !isWildcardType() && !isWildcardSubtype();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the character set, as indicated by a <code>charset</code> parameter, if any.
|
||||
* @return the character set; or <code>null</code> if not available
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2011 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,6 +16,7 @@
|
|||
|
||||
package org.springframework.web;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
/**
|
||||
|
|
@ -49,6 +50,15 @@ public class HttpRequestMethodNotSupportedException extends ServletException {
|
|||
this(method, supportedMethods, "Request method '" + method + "' not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new HttpRequestMethodNotSupportedException.
|
||||
* @param method the unsupported HTTP request method
|
||||
* @param supportedMethods the actually supported HTTP methods
|
||||
*/
|
||||
public HttpRequestMethodNotSupportedException(String method, Collection<String> supportedMethods) {
|
||||
this(method, supportedMethods.toArray(new String[supportedMethods.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new HttpRequestMethodNotSupportedException.
|
||||
* @param method the unsupported HTTP request method
|
||||
|
|
|
|||
|
|
@ -16,12 +16,6 @@
|
|||
|
||||
package org.springframework.http;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
|
@ -30,9 +24,12 @@ import java.util.List;
|
|||
import java.util.Random;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
|
|
@ -500,4 +497,13 @@ public class MediaTypeTests {
|
|||
assertEquals(mediaType, conversionService.convert("application/xml", MediaType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isConcrete() {
|
||||
assertTrue("text/plain not concrete", MediaType.TEXT_PLAIN.isConcrete());
|
||||
assertFalse("*/* concrete", MediaType.ALL.isConcrete());
|
||||
assertFalse("text/* concrete", new MediaType("text", "*").isConcrete());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue