diff --git a/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java b/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java index 5fbe4ff3c46..38a55fbb270 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java +++ b/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java @@ -149,7 +149,7 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { @Override public DefaultBuilder additionalMessageConverter(HttpMessageConverter customConverter) { Assert.notNull(customConverter, "'customConverter' must not be null"); - this.commonMessageConverters.additionalMessageConverters.add(customConverter); + this.commonMessageConverters.customMessageConverters.add(customConverter); return this; } @@ -216,7 +216,13 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { private @Nullable HttpMessageConverter yamlMessageConverter; - private final List> additionalMessageConverters = new ArrayList<>(); + private @Nullable HttpMessageConverter protobufMessageConverter; + + private @Nullable HttpMessageConverter atomMessageConverter; + + private @Nullable HttpMessageConverter rssMessageConverter; + + private final List> customMessageConverters = new ArrayList<>(); static { ClassLoader classLoader = DefaultClientMessageConverterConfigurer.class.getClassLoader(); @@ -340,13 +346,34 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { this.inheritedMessageConverters.xmlMessageConverter != null) { converters.add(this.inheritedMessageConverters.xmlMessageConverter); } + if (this.protobufMessageConverter != null) { + converters.add(this.protobufMessageConverter); + } + else if (this.inheritedMessageConverters != null && + this.inheritedMessageConverters.protobufMessageConverter != null) { + converters.add(this.inheritedMessageConverters.protobufMessageConverter); + } + if (this.atomMessageConverter != null) { + converters.add(this.atomMessageConverter); + } + else if (this.inheritedMessageConverters != null && + this.inheritedMessageConverters.atomMessageConverter != null) { + converters.add(this.inheritedMessageConverters.atomMessageConverter); + } + if (this.rssMessageConverter != null) { + converters.add(this.rssMessageConverter); + } + else if (this.inheritedMessageConverters != null && + this.inheritedMessageConverters.rssMessageConverter != null) { + converters.add(this.inheritedMessageConverters.rssMessageConverter); + } return converters; } List> getCustomConverters() { - List> result = new ArrayList<>(this.additionalMessageConverters); + List> result = new ArrayList<>(this.customMessageConverters); if (this.inheritedMessageConverters != null) { - result.addAll(this.inheritedMessageConverters.additionalMessageConverters); + result.addAll(this.inheritedMessageConverters.customMessageConverters); } return result; } @@ -405,12 +432,12 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { } if (isKotlinSerializationProtobufPresent) { - this.additionalMessageConverters.add(new KotlinSerializationProtobufHttpMessageConverter()); + this.protobufMessageConverter = new KotlinSerializationProtobufHttpMessageConverter(); } if (isRomePresent) { - this.additionalMessageConverters.add(new AtomFeedHttpMessageConverter()); - this.additionalMessageConverters.add(new RssChannelHttpMessageConverter()); + this.atomMessageConverter = new AtomFeedHttpMessageConverter(); + this.rssMessageConverter = new RssChannelHttpMessageConverter(); } } @@ -466,7 +493,7 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { @Override public ClientMessageConverterConfigurer additionalMessageConverter(HttpMessageConverter customConverter) { Assert.notNull(customConverter, "'customConverter' must not be null"); - this.clientMessageConverters.additionalMessageConverters.add(customConverter); + this.clientMessageConverters.customMessageConverters.add(customConverter); return this; } @@ -485,16 +512,16 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { List> allConverters = new ArrayList<>(); List> partConverters = new ArrayList<>(); - partConverters.addAll(this.clientMessageConverters.getCoreConverters()); partConverters.addAll(this.clientMessageConverters.getCustomConverters()); + partConverters.addAll(this.clientMessageConverters.getCoreConverters()); + allConverters.addAll(this.clientMessageConverters.getCustomConverters()); allConverters.addAll(this.clientMessageConverters.getBaseConverters()); allConverters.addAll(this.resourceMessageConverters); if (!partConverters.isEmpty()) { allConverters.add(new AllEncompassingFormHttpMessageConverter(partConverters)); } allConverters.addAll(this.clientMessageConverters.getCoreConverters()); - allConverters.addAll(this.clientMessageConverters.getCustomConverters()); if (this.configurer != null) { allConverters.forEach(this.configurer); @@ -553,7 +580,7 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { @Override public ServerMessageConverterConfigurer additionalMessageConverter(HttpMessageConverter customConverter) { Assert.notNull(customConverter, "'customConverter' must not be null"); - this.serverMessageConverters.additionalMessageConverters.add(customConverter); + this.serverMessageConverters.customMessageConverters.add(customConverter); return this; } @@ -572,16 +599,16 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { List> allConverters = new ArrayList<>(); List> partConverters = new ArrayList<>(); - partConverters.addAll(this.serverMessageConverters.getCoreConverters()); partConverters.addAll(this.serverMessageConverters.getCustomConverters()); + partConverters.addAll(this.serverMessageConverters.getCoreConverters()); + allConverters.addAll(this.serverMessageConverters.getCustomConverters()); allConverters.addAll(this.serverMessageConverters.getBaseConverters()); allConverters.addAll(this.resourceMessageConverters); if (!partConverters.isEmpty()) { allConverters.add(new AllEncompassingFormHttpMessageConverter(partConverters)); } allConverters.addAll(this.serverMessageConverters.getCoreConverters()); - allConverters.addAll(this.serverMessageConverters.getCustomConverters()); if (this.configurer != null) { allConverters.forEach(this.configurer); } diff --git a/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java b/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java index 9d86fee1b5f..9ff9f07274c 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java +++ b/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java @@ -31,6 +31,7 @@ import java.util.function.Consumer; *

The following HTTP message converters will be detected and registered if available, in order. * For {@link #forClient() client side converters}: *

    + *
  1. All custom message converters configured with the builder *
  2. {@link ByteArrayHttpMessageConverter} *
  3. {@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset *
  4. {@link ResourceHttpMessageConverter}, with resource streaming support disabled @@ -42,11 +43,11 @@ import java.util.function.Consumer; *
  5. An XML converter *
  6. An ProtoBuf converter *
  7. ATOM and RSS converters - *
  8. All custom message converters configured with the builder *
* * For {@link #forClient() client side converters}: *
    + *
  1. All custom message converters configured with the builder *
  2. {@link ByteArrayHttpMessageConverter} *
  3. {@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset *
  4. {@link ResourceHttpMessageConverter} @@ -58,7 +59,6 @@ import java.util.function.Consumer; *
  5. An XML converter *
  6. An ProtoBuf converter *
  7. ATOM and RSS converters - *
  8. All custom message converters configured with the builder *
  9. a Multipart converter, using all detected and custom converters for part conversion *
* diff --git a/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java b/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java index ef2a95a03e1..1598ee4bb24 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java @@ -150,7 +150,21 @@ class DefaultHttpMessageConvertersTests { void registerCustomMessageConverter() { var converters = HttpMessageConverters.create() .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - assertThat(converters.forClient()).hasExactlyElementsOfTypes(AllEncompassingFormHttpMessageConverter.class, CustomHttpMessageConverter.class); + assertThat(converters.forClient()).hasExactlyElementsOfTypes(CustomHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class); + } + + @Test + void registerCustomMessageConverterAheadOfDefaults() { + var converters = HttpMessageConverters.withDefaults() + .additionalMessageConverter(new CustomHttpMessageConverter()).build(); + assertThat(converters.forClient()).hasExactlyElementsOfTypes( + CustomHttpMessageConverter.class, ByteArrayHttpMessageConverter.class, + StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, + AllEncompassingFormHttpMessageConverter.class, + JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class, + JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class, + JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class, + AtomFeedHttpMessageConverter.class, RssChannelHttpMessageConverter.class); } @Test @@ -224,7 +238,22 @@ class DefaultHttpMessageConvertersTests { void registerCustomMessageConverter() { var converters = HttpMessageConverters.create() .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - assertThat(converters.forServer()).hasExactlyElementsOfTypes(AllEncompassingFormHttpMessageConverter.class, CustomHttpMessageConverter.class); + assertThat(converters.forServer()).hasExactlyElementsOfTypes(CustomHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class); + } + + @Test + void registerCustomMessageConverterAheadOfDefaults() { + var converters = HttpMessageConverters.withDefaults() + .additionalMessageConverter(new CustomHttpMessageConverter()).build(); + assertThat(converters.forServer()).hasExactlyElementsOfTypes( + CustomHttpMessageConverter.class, + ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, + ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class, + AllEncompassingFormHttpMessageConverter.class, + JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class, + JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class, + JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class, + AtomFeedHttpMessageConverter.class, RssChannelHttpMessageConverter.class); } @Test