SPR-7353 - @ResponseBody and returned HttpEntity now respect @RequestMapping.produces()
This commit is contained in:
@@ -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,9 +50,9 @@ 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
|
||||
* @since 3.1.0
|
||||
@@ -84,10 +88,10 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
||||
this.mappedInterceptors = MappedInterceptors.createFromDeclaredBeans(getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@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,14 +140,13 @@ 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
|
||||
protected Set<String> getMappingPaths(RequestMappingInfo mapping) {
|
||||
return mapping.getPatterns();
|
||||
@@ -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,30 +17,38 @@
|
||||
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
|
||||
* @since 3.1
|
||||
@@ -48,103 +56,161 @@ 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")
|
||||
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
|
||||
MethodParameter methodParam,
|
||||
Class<T> paramType)
|
||||
Class<T> paramType)
|
||||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
|
||||
HttpInputMessage inputMessage = createInputMessage(webRequest);
|
||||
|
||||
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,
|
||||
HttpInputMessage inputMessage,
|
||||
protected <T> void writeWithMessageConverters(T returnValue,
|
||||
MethodParameter returnType,
|
||||
HttpInputMessage inputMessage,
|
||||
HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMediaTypeNotAcceptableException {
|
||||
|
||||
List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(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);
|
||||
Set<MediaType> producibleMediaTypes = getProducibleMediaTypes(returnType.getMethod(), returnValue.getClass());
|
||||
Set<MediaType> acceptableMediaTypes = getAcceptableMediaTypes(inputMessage);
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -97,17 +94,12 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
throw new IllegalArgumentException(
|
||||
"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
|
||||
@@ -60,25 +56,20 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest,
|
||||
NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory)
|
||||
throws IOException, HttpMediaTypeNotSupportedException {
|
||||
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,
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,5 +78,5 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
|
||||
return new ServletServerHttpResponse(servletResponse);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user