From 3ac7f839007cc1c2c16d3e38de3f2df725bc6465 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 12 Nov 2025 15:50:15 +0100 Subject: [PATCH] Relax media type checks in HttpMessageConverters Prior to this commit, `HttpMessageConverters` would assert that the given converter in `withXmlConverter` has a media type that is equal to "application/xml" in its list of supported converters. This approach would not work if the given converter supports "application/xml;charset=UTF-8" because of the strict equal check being performed. This commit ensures that we only consider the type and subtype of the considered media types when comparing, removing the parameters from the picture. Fixes gh-35801 --- .../DefaultHttpMessageConverters.java | 27 ++++++++++--------- .../DefaultHttpMessageConvertersTests.java | 17 +++++++----- 2 files changed, 26 insertions(+), 18 deletions(-) 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 1df9a640e14..1a26688e5e9 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 @@ -162,8 +162,7 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { void setStringConverter(HttpMessageConverter stringConverter) { - Assert.isTrue(stringConverter.getSupportedMediaTypes().contains(MediaType.TEXT_PLAIN), - "stringConverter should support 'text/plain'"); + checkConverterSupports(stringConverter, MediaType.TEXT_PLAIN); this.stringConverter = stringConverter; } @@ -173,20 +172,17 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { } void setJsonConverter(HttpMessageConverter jsonConverter) { - Assert.isTrue(jsonConverter.getSupportedMediaTypes().contains(MediaType.APPLICATION_JSON), - "jsonConverter should support 'application/json'"); + checkConverterSupports(jsonConverter, MediaType.APPLICATION_JSON); this.jsonConverter = jsonConverter; } void setXmlConverter(HttpMessageConverter xmlConverter) { - Assert.isTrue(xmlConverter.getSupportedMediaTypes().contains(MediaType.TEXT_XML), - "xmlConverter should support 'text/xml'"); + checkConverterSupports(xmlConverter, MediaType.TEXT_XML); this.xmlConverter = xmlConverter; } void setSmileConverter(HttpMessageConverter smileConverter) { - Assert.isTrue(smileConverter.getSupportedMediaTypes().contains(new MediaType("application", "x-jackson-smile")), - "smileConverter should support 'application/x-jackson-smile'"); + checkConverterSupports(smileConverter, new MediaType("application", "x-jackson-smile")); this.smileConverter = smileConverter; } @@ -196,17 +192,24 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { } void setCborConverter(HttpMessageConverter cborConverter) { - Assert.isTrue(cborConverter.getSupportedMediaTypes().contains(MediaType.APPLICATION_CBOR), - "cborConverter should support 'application/cbor'"); + checkConverterSupports(cborConverter, MediaType.APPLICATION_CBOR); this.cborConverter = cborConverter; } void setYamlConverter(HttpMessageConverter yamlConverter) { - Assert.isTrue(yamlConverter.getSupportedMediaTypes().contains(MediaType.APPLICATION_YAML), - "yamlConverter should support 'application/yaml'"); + checkConverterSupports(yamlConverter, MediaType.APPLICATION_YAML); this.yamlConverter = yamlConverter; } + private void checkConverterSupports(HttpMessageConverter converter, MediaType mediaType) { + for (MediaType supportedMediaType : converter.getSupportedMediaTypes()) { + if (mediaType.equalsTypeAndSubtype(supportedMediaType)) { + return; + } + } + throw new IllegalArgumentException("converter should support '" + mediaType + "'"); + } + void addCustomMessageConverter(HttpMessageConverter customConverter) { Assert.notNull(customConverter, "'customConverter' must not be null"); this.customConverters.add(customConverter); 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 8b56a3be5bb..f939e9b227a 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 @@ -65,42 +65,47 @@ class DefaultHttpMessageConvertersTests { void failsWhenStringConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() .isThrownBy(() -> HttpMessageConverters.forClient().withStringConverter(new CustomHttpMessageConverter()).build()) - .withMessage("stringConverter should support 'text/plain'"); + .withMessage("converter should support 'text/plain'"); } @Test void failsWhenJsonConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() .isThrownBy(() -> HttpMessageConverters.forClient().withJsonConverter(new CustomHttpMessageConverter()).build()) - .withMessage("jsonConverter should support 'application/json'"); + .withMessage("converter should support 'application/json'"); + } + + @Test + void canConfigureXmlConverterWithCharset() { + HttpMessageConverters.forClient().withXmlConverter(new JacksonXmlHttpMessageConverter()).build(); } @Test void failsWhenXmlConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() .isThrownBy(() -> HttpMessageConverters.forClient().withXmlConverter(new CustomHttpMessageConverter()).build()) - .withMessage("xmlConverter should support 'text/xml'"); + .withMessage("converter should support 'text/xml'"); } @Test void failsWhenSmileConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() .isThrownBy(() -> HttpMessageConverters.forClient().withSmileConverter(new CustomHttpMessageConverter()).build()) - .withMessage("smileConverter should support 'application/x-jackson-smile'"); + .withMessage("converter should support 'application/x-jackson-smile'"); } @Test void failsWhenCborConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() .isThrownBy(() -> HttpMessageConverters.forClient().withCborConverter(new CustomHttpMessageConverter()).build()) - .withMessage("cborConverter should support 'application/cbor'"); + .withMessage("converter should support 'application/cbor'"); } @Test void failsWhenYamlConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() .isThrownBy(() -> HttpMessageConverters.forClient().withYamlConverter(new CustomHttpMessageConverter()).build()) - .withMessage("yamlConverter should support 'application/yaml'"); + .withMessage("converter should support 'application/yaml'"); }