diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HandlerMapping.java
index c95e6ede0f9..8c381b6c469 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HandlerMapping.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/HandlerMapping.java
@@ -92,6 +92,14 @@ public interface HandlerMapping {
*/
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
+ /**
+ * Name of the {@link HttpServletRequest} attribute that contains the set of producible MediaTypes
+ * applicable to the mapped handler.
+ *
Note: This attribute is not required to be supported by all HandlerMapping implementations.
+ * Handlers should not necessarily expect this request attribute to be present in all scenarios.
+ */
+ String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
+
/**
* Return a handler and any interceptors for this request. The choice may be made
* on request URL, session state, or any factor the implementing class chooses.
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
index c8611a31f8c..10eda4812f4 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
@@ -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;
@@ -173,11 +174,17 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping uriTemplateVariables = pathMatcher.extractUriTemplateVariables(pattern, lookupPath);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
+
+ Set mediaTypes = info.getProduces().getMediaTypes();
+ if (mediaTypes.size() > 1 || !MediaType.ALL.equals(mediaTypes.iterator().next())) {
+ request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
+ }
}
/**
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java
index 127ad0ee696..4e714e6eb2f 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/AbstractMessageConverterMethodProcessor.java
@@ -17,18 +17,17 @@
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;
@@ -37,12 +36,13 @@ 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.CollectionUtils;
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;
+import org.springframework.web.servlet.HandlerMapping;
/**
* A base class for resolving method argument values by reading from the body of a request with {@link
@@ -124,22 +124,43 @@ public abstract class AbstractMessageConverterMethodProcessor
* @return the output message
*/
protected HttpOutputMessage createOutputMessage(NativeWebRequest webRequest) {
- HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
- return new ServletServerHttpResponse(servletResponse);
+ HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
+ return new ServletServerHttpResponse(response);
+ }
+
+ /**
+ * Returns the media types that can be produced:
+ *
+ * - The set of producible media types specified in the request mappings, or
+ *
- The set of supported media types by all configured message converters, or
+ *
- {@link MediaType#ALL}
+ */
+ @SuppressWarnings("unchecked")
+ protected Set getProducibleMediaTypes(NativeWebRequest webRequest) {
+ HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
+ Set mediaTypes = (Set) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
+ if (!CollectionUtils.isEmpty(mediaTypes)) {
+ return mediaTypes;
+ }
+ else if (!allSupportedMediaTypes.isEmpty()) {
+ return new HashSet(allSupportedMediaTypes);
+ }
+ else {
+ return Collections.singleton(MediaType.ALL);
+ }
+
}
@SuppressWarnings("unchecked")
protected void writeWithMessageConverters(T returnValue,
MethodParameter returnType,
HttpInputMessage inputMessage,
- HttpOutputMessage outputMessage)
+ HttpOutputMessage outputMessage,
+ Set producibleMediaTypes)
throws IOException, HttpMediaTypeNotAcceptableException {
- Set producibleMediaTypes = getProducibleMediaTypes(returnType.getMethod(), returnValue.getClass());
- Set acceptableMediaTypes = getAcceptableMediaTypes(inputMessage);
-
List mediaTypes = new ArrayList();
- for (MediaType acceptableMediaType : acceptableMediaTypes) {
+ for (MediaType acceptableMediaType : getAcceptableMediaTypes(inputMessage)) {
for (MediaType producibleMediaType : producibleMediaTypes) {
if (acceptableMediaType.isCompatibleWith(producibleMediaType)) {
mediaTypes.add(getMostSpecificMediaType(acceptableMediaType, producibleMediaType));
@@ -173,33 +194,7 @@ public abstract class AbstractMessageConverterMethodProcessor
}
}
}
- else {
- throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
- }
- }
-
- private Set getProducibleMediaTypes(Method handlerMethod, Class> returnValueClass) {
- RequestMapping requestMappingAnn = handlerMethod.getAnnotation(RequestMapping.class);
- if (requestMappingAnn == null) {
- requestMappingAnn = handlerMethod.getClass().getAnnotation(RequestMapping.class);
- }
- Set result = new HashSet();
- 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;
+ throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
}
private Set getAcceptableMediaTypes(HttpInputMessage inputMessage) {
@@ -209,7 +204,7 @@ public abstract class AbstractMessageConverterMethodProcessor
}
return result;
}
-
+
private MediaType getMostSpecificMediaType(MediaType type1, MediaType type2) {
return MediaType.SPECIFICITY_COMPARATOR.compare(type1, type2) < 0 ? type1 : type2;
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java
index 31dd5a2f127..cd2864395d6 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessor.java
@@ -22,12 +22,14 @@ import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
+import java.util.Set;
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.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpResponse;
@@ -121,7 +123,8 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
Object body = responseEntity.getBody();
if (body != null) {
- writeWithMessageConverters(body, returnType, createInputMessage(webRequest), outputMessage);
+ Set mediaTypes = getProducibleMediaTypes(webRequest);
+ writeWithMessageConverters(body, returnType, createInputMessage(webRequest), outputMessage, mediaTypes);
}
else {
// flush headers to the HttpServletResponse
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java
index 1d55a21472d..ebed2023603 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessor.java
@@ -18,10 +18,14 @@ package org.springframework.web.servlet.mvc.method.annotation.support;
import java.io.IOException;
import java.util.List;
+import java.util.Set;
+
import javax.servlet.http.HttpServletResponse;
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.ServletServerHttpResponse;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
@@ -68,8 +72,10 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
mavContainer.setResolveView(false);
if (returnValue != null) {
- writeWithMessageConverters(returnValue, returnType, createInputMessage(webRequest),
- createOutputMessage(webRequest));
+ HttpInputMessage inputMessage = createInputMessage(webRequest);
+ HttpOutputMessage outputMessage = createOutputMessage(webRequest);
+ Set mediaTypes = getProducibleMediaTypes(webRequest);
+ writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage, mediaTypes);
}
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java
index 28c9281931b..8aa4ac39084 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/HttpEntityMethodProcessorTests.java
@@ -15,6 +15,7 @@
*/
package org.springframework.web.servlet.mvc.method.annotation.support;
+import static org.springframework.web.servlet.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -40,6 +41,7 @@ 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.HandlerMapping;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
@@ -162,8 +164,6 @@ 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);
@@ -180,6 +180,7 @@ public class HttpEntityMethodProcessorTests {
ResponseEntity returnValue = new ResponseEntity(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));
@@ -244,8 +245,6 @@ public class HttpEntityMethodProcessorTests {
ResponseEntity returnValue = new ResponseEntity("body", responseHeaders, HttpStatus.ACCEPTED);
Capture outputMessage = new Capture();
- 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);
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java
index 6db478b9b5c..2c106155dd1 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestResponseBodyMethodProcessorTests.java
@@ -38,6 +38,7 @@ 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 org.springframework.web.servlet.HandlerMapping;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
@@ -151,8 +152,6 @@ 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);
@@ -168,6 +167,7 @@ public class RequestResponseBodyMethodProcessorTests {
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));