Avoid duplicate Accept header values in RestTemplate

Prior to this commit, the various `HttpMessageConverter` instances
configured for a given `RestTemplate` instance could all contribute
`MediaType` values to the "Accept:" request header.

This could lead to duplicate media types in that request header,
cluttering for the HTTP request for no reason.

This commit ensures that only distinct values are added to the request.

Issue: SPR-16690
This commit is contained in:
Brian Clozel
2018-04-12 21:56:44 +02:00
parent 0efa7a05ad
commit 69e3fde295
2 changed files with 250 additions and 405 deletions

View File

@@ -25,6 +25,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
@@ -836,45 +838,43 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (this.responseType != null) {
Class<?> responseClass = null;
if (this.responseType instanceof Class) {
responseClass = (Class<?>) this.responseType;
}
List<MediaType> allSupportedMediaTypes = new ArrayList<>();
for (HttpMessageConverter<?> converter : getMessageConverters()) {
if (responseClass != null) {
if (converter.canRead(responseClass, null)) {
allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter));
}
}
else if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(this.responseType, null, null)) {
allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter));
}
}
}
if (!allSupportedMediaTypes.isEmpty()) {
MediaType.sortBySpecificity(allSupportedMediaTypes);
if (logger.isDebugEnabled()) {
logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
final Class<?> responseClass = (this.responseType instanceof Class) ?
(Class<?>) this.responseType : null;
final List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
.filter(converter -> canReadResponse(responseClass, converter))
.flatMap(this::getSupportedMediaTypes)
.distinct()
.sorted(MediaType.SPECIFICITY_COMPARATOR)
.collect(Collectors.toList());
if (logger.isDebugEnabled()) {
logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
private List<MediaType> getSupportedMediaTypes(HttpMessageConverter<?> messageConverter) {
List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
List<MediaType> result = new ArrayList<>(supportedMediaTypes.size());
for (MediaType supportedMediaType : supportedMediaTypes) {
if (supportedMediaType.getCharset() != null) {
supportedMediaType =
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
}
result.add(supportedMediaType);
private boolean canReadResponse(@Nullable Class<?> responseClass, HttpMessageConverter<?> converter) {
if (responseClass != null) {
return converter.canRead(responseClass, null);
}
return result;
else if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter =
(GenericHttpMessageConverter<?>) converter;
return genericConverter
.canRead(this.responseType, null, null);
}
return false;
}
private Stream<MediaType> getSupportedMediaTypes(HttpMessageConverter<?> messageConverter) {
return messageConverter.getSupportedMediaTypes()
.stream()
.map(mediaType -> {
if (mediaType.getCharset() != null) {
return new MediaType(mediaType.getType(), mediaType.getSubtype());
}
return mediaType;
});
}
}