|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2012 the original author or authors. |
|
|
|
* Copyright 2002-2014 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -22,7 +22,6 @@ import java.util.Collections; |
|
|
|
import java.util.LinkedHashSet; |
|
|
|
import java.util.LinkedHashSet; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.Set; |
|
|
|
|
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
|
|
|
|
|
|
|
|
@ -41,8 +40,8 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler; |
|
|
|
import org.springframework.web.servlet.HandlerMapping; |
|
|
|
import org.springframework.web.servlet.HandlerMapping; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Extends {@link AbstractMessageConverterMethodArgumentResolver} with the ability to handle method return |
|
|
|
* Extends {@link AbstractMessageConverterMethodArgumentResolver} with the ability to handle |
|
|
|
* values by writing to the response with {@link HttpMessageConverter}s. |
|
|
|
* method return values by writing to the response with {@link HttpMessageConverter}s. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Arjen Poutsma |
|
|
|
* @author Arjen Poutsma |
|
|
|
* @author Rossen Stoyanchev |
|
|
|
* @author Rossen Stoyanchev |
|
|
|
@ -55,6 +54,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
|
|
|
|
|
|
|
|
private final ContentNegotiationManager contentNegotiationManager; |
|
|
|
private final ContentNegotiationManager contentNegotiationManager; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters) { |
|
|
|
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> messageConverters) { |
|
|
|
this(messageConverters, null); |
|
|
|
this(messageConverters, null); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -63,12 +63,12 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
ContentNegotiationManager manager) { |
|
|
|
ContentNegotiationManager manager) { |
|
|
|
|
|
|
|
|
|
|
|
super(messageConverters); |
|
|
|
super(messageConverters); |
|
|
|
this.contentNegotiationManager = (manager != null) ? manager : new ContentNegotiationManager(); |
|
|
|
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}. |
|
|
|
* Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}. |
|
|
|
* |
|
|
|
|
|
|
|
* @param webRequest the web request to create an output message from |
|
|
|
* @param webRequest the web request to create an output message from |
|
|
|
* @return the output message |
|
|
|
* @return the output message |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -81,10 +81,9 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
* Writes the given return value to the given web request. Delegates to |
|
|
|
* Writes the given return value to the given web request. Delegates to |
|
|
|
* {@link #writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse)} |
|
|
|
* {@link #writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse)} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected <T> void writeWithMessageConverters(T returnValue, |
|
|
|
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest) |
|
|
|
MethodParameter returnType, |
|
|
|
|
|
|
|
NativeWebRequest webRequest) |
|
|
|
|
|
|
|
throws IOException, HttpMediaTypeNotAcceptableException { |
|
|
|
throws IOException, HttpMediaTypeNotAcceptableException { |
|
|
|
|
|
|
|
|
|
|
|
ServletServerHttpRequest inputMessage = createInputMessage(webRequest); |
|
|
|
ServletServerHttpRequest inputMessage = createInputMessage(webRequest); |
|
|
|
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); |
|
|
|
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); |
|
|
|
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); |
|
|
|
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); |
|
|
|
@ -92,7 +91,6 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Writes the given return type to the given output message. |
|
|
|
* Writes the given return type to the given output message. |
|
|
|
* |
|
|
|
|
|
|
|
* @param returnValue the value to write to the output message |
|
|
|
* @param returnValue the value to write to the output message |
|
|
|
* @param returnType the type of the value |
|
|
|
* @param returnType the type of the value |
|
|
|
* @param inputMessage the input messages. Used to inspect the {@code Accept} header. |
|
|
|
* @param inputMessage the input messages. Used to inspect the {@code Accept} header. |
|
|
|
@ -102,23 +100,20 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
* the request cannot be met by the message converters |
|
|
|
* the request cannot be met by the message converters |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
protected <T> void writeWithMessageConverters(T returnValue, |
|
|
|
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, |
|
|
|
MethodParameter returnType, |
|
|
|
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) |
|
|
|
ServletServerHttpRequest inputMessage, |
|
|
|
|
|
|
|
ServletServerHttpResponse outputMessage) |
|
|
|
|
|
|
|
throws IOException, HttpMediaTypeNotAcceptableException { |
|
|
|
throws IOException, HttpMediaTypeNotAcceptableException { |
|
|
|
|
|
|
|
|
|
|
|
Class<?> returnValueClass = returnValue.getClass(); |
|
|
|
Class<?> returnValueClass = returnValue.getClass(); |
|
|
|
|
|
|
|
|
|
|
|
HttpServletRequest servletRequest = inputMessage.getServletRequest(); |
|
|
|
HttpServletRequest servletRequest = inputMessage.getServletRequest(); |
|
|
|
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest); |
|
|
|
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest); |
|
|
|
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass); |
|
|
|
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass); |
|
|
|
|
|
|
|
|
|
|
|
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>(); |
|
|
|
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>(); |
|
|
|
for (MediaType r : requestedMediaTypes) { |
|
|
|
for (MediaType requestedType : requestedMediaTypes) { |
|
|
|
for (MediaType p : producibleMediaTypes) { |
|
|
|
for (MediaType producibleType : producibleMediaTypes) { |
|
|
|
if (r.isCompatibleWith(p)) { |
|
|
|
if (requestedType.isCompatibleWith(producibleType)) { |
|
|
|
compatibleMediaTypes.add(getMostSpecificMediaType(r, p)); |
|
|
|
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -143,7 +138,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
|
|
|
|
|
|
|
|
if (selectedMediaType != null) { |
|
|
|
if (selectedMediaType != null) { |
|
|
|
selectedMediaType = selectedMediaType.removeQualityValue(); |
|
|
|
selectedMediaType = selectedMediaType.removeQualityValue(); |
|
|
|
for (HttpMessageConverter<?> messageConverter : messageConverters) { |
|
|
|
for (HttpMessageConverter<?> messageConverter : this.messageConverters) { |
|
|
|
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { |
|
|
|
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { |
|
|
|
((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage); |
|
|
|
((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage); |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
@ -154,15 +149,15 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); |
|
|
|
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Returns the media types that can be produced: |
|
|
|
* Returns the media types that can be produced: |
|
|
|
* <ul> |
|
|
|
* <ul> |
|
|
|
* <li>The producible media types specified in the request mappings, or |
|
|
|
* <li>The producible media types specified in the request mappings, or |
|
|
|
* <li>Media types of configured converters that can write the specific return value, or |
|
|
|
* <li>Media types of configured converters that can write the specific return value, or |
|
|
|
* <li>{@link MediaType#ALL} |
|
|
|
* <li>{@link MediaType#ALL} |
|
|
|
* </ul> |
|
|
|
* </ul> |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@ -171,9 +166,9 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
if (!CollectionUtils.isEmpty(mediaTypes)) { |
|
|
|
if (!CollectionUtils.isEmpty(mediaTypes)) { |
|
|
|
return new ArrayList<MediaType>(mediaTypes); |
|
|
|
return new ArrayList<MediaType>(mediaTypes); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (!allSupportedMediaTypes.isEmpty()) { |
|
|
|
else if (!this.allSupportedMediaTypes.isEmpty()) { |
|
|
|
List<MediaType> result = new ArrayList<MediaType>(); |
|
|
|
List<MediaType> result = new ArrayList<MediaType>(); |
|
|
|
for (HttpMessageConverter<?> converter : messageConverters) { |
|
|
|
for (HttpMessageConverter<?> converter : this.messageConverters) { |
|
|
|
if (converter.canWrite(returnValueClass, null)) { |
|
|
|
if (converter.canWrite(returnValueClass, null)) { |
|
|
|
result.addAll(converter.getSupportedMediaTypes()); |
|
|
|
result.addAll(converter.getSupportedMediaTypes()); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -187,7 +182,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
|
|
|
|
|
|
|
|
private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException { |
|
|
|
private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException { |
|
|
|
List<MediaType> mediaTypes = this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request)); |
|
|
|
List<MediaType> mediaTypes = this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request)); |
|
|
|
return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes; |
|
|
|
return (mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -195,8 +190,8 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe |
|
|
|
* with the q-value of the former. |
|
|
|
* with the q-value of the former. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) { |
|
|
|
private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) { |
|
|
|
produceType = produceType.copyQualityValue(acceptType); |
|
|
|
MediaType produceTypeToUse = produceType.copyQualityValue(acceptType); |
|
|
|
return MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceType) <= 0 ? acceptType : produceType; |
|
|
|
return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|