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
Closes gh-22401
This commit is contained in:
Brian Clozel
2018-04-12 21:56:44 +02:00
parent ddb5b4a8c5
commit a0d420686b
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;
@@ -795,45 +797,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;
});
}
}