Simplify media files detection in WebMvcConfigurationSupport
Prior to this commit, `WebMvcConfigurationSupport` would configure file extensions/media types registrations based on classpath detection. Since gh-33894, the detection of message converters is located in a single place, `HttpMessageConverters`. This commit updates the `WebMvcConfigurationSupport` to use the actual message converters configured to decide which file extensions should be set up for content negotiation. See gh-33894
This commit is contained in:
parent
02ff681c73
commit
28f9adf88e
|
@ -16,11 +16,14 @@
|
|||
|
||||
package org.springframework.web.servlet.config.annotation;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
@ -177,63 +180,19 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
|||
*/
|
||||
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
|
||||
|
||||
private static final boolean romePresent;
|
||||
|
||||
private static final boolean jaxb2Present;
|
||||
|
||||
private static final boolean jacksonPresent;
|
||||
|
||||
private static final boolean jackson2Present;
|
||||
|
||||
private static final boolean jacksonXmlPresent;
|
||||
|
||||
private static final boolean jackson2XmlPresent;
|
||||
|
||||
private static final boolean jacksonSmilePresent;
|
||||
|
||||
private static final boolean jackson2SmilePresent;
|
||||
|
||||
private static final boolean jacksonCborPresent;
|
||||
|
||||
private static final boolean jackson2CborPresent;
|
||||
|
||||
private static final boolean jacksonYamlPresent;
|
||||
|
||||
private static final boolean jackson2YamlPresent;
|
||||
|
||||
private static final boolean gsonPresent;
|
||||
|
||||
private static final boolean jsonbPresent;
|
||||
|
||||
private static final boolean kotlinSerializationPresent;
|
||||
|
||||
private static final boolean kotlinSerializationCborPresent;
|
||||
|
||||
private static final boolean kotlinSerializationJsonPresent;
|
||||
|
||||
private static final boolean kotlinSerializationProtobufPresent;
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
|
||||
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
|
||||
jaxb2Present = ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);
|
||||
jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", classLoader);
|
||||
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||
jacksonXmlPresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.xml.XmlMapper", classLoader);
|
||||
jackson2XmlPresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
|
||||
jacksonSmilePresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.smile.SmileMapper", classLoader);
|
||||
jackson2SmilePresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
|
||||
jacksonCborPresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.cbor.CBORMapper", classLoader);
|
||||
jackson2CborPresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
|
||||
jacksonYamlPresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.yaml.YAMLMapper", classLoader);
|
||||
jackson2YamlPresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", classLoader);
|
||||
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
|
||||
jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader);
|
||||
kotlinSerializationPresent = ClassUtils.isPresent("kotlinx.serialization.Serializable", classLoader);
|
||||
kotlinSerializationCborPresent = kotlinSerializationPresent && ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", classLoader);
|
||||
kotlinSerializationJsonPresent = kotlinSerializationPresent && ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader);
|
||||
kotlinSerializationProtobufPresent = kotlinSerializationPresent && ClassUtils.isPresent("kotlinx.serialization.protobuf.ProtoBuf", classLoader);
|
||||
}
|
||||
|
||||
|
||||
|
@ -444,23 +403,32 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
|||
|
||||
protected Map<String, MediaType> getDefaultMediaTypes() {
|
||||
Map<String, MediaType> map = new HashMap<>(4);
|
||||
if (romePresent) {
|
||||
List<HttpMessageConverter<?>> messageConverters = getMessageConverters();
|
||||
Set<MediaType> supportedMediaTypes = messageConverters.stream()
|
||||
.flatMap(converter -> converter.getSupportedMediaTypes().stream())
|
||||
.collect(Collectors.toSet());
|
||||
if (supportedMediaTypes.contains(MediaType.APPLICATION_ATOM_XML)) {
|
||||
map.put("atom", MediaType.APPLICATION_ATOM_XML);
|
||||
}
|
||||
if (supportedMediaTypes.contains(MediaType.APPLICATION_RSS_XML)) {
|
||||
map.put("rss", MediaType.APPLICATION_RSS_XML);
|
||||
}
|
||||
if (jaxb2Present || jacksonXmlPresent || jackson2XmlPresent) {
|
||||
MediaType xmlUtf8MediaType = new MediaType("application", "xml", StandardCharsets.UTF_8);
|
||||
if (supportedMediaTypes.contains(MediaType.APPLICATION_XML) ||
|
||||
supportedMediaTypes.contains(xmlUtf8MediaType)) {
|
||||
map.put("xml", MediaType.APPLICATION_XML);
|
||||
}
|
||||
if (jacksonPresent || jackson2Present || gsonPresent || jsonbPresent || kotlinSerializationJsonPresent) {
|
||||
if (supportedMediaTypes.contains(MediaType.APPLICATION_JSON)) {
|
||||
map.put("json", MediaType.APPLICATION_JSON);
|
||||
}
|
||||
if (jacksonSmilePresent || jackson2SmilePresent) {
|
||||
map.put("smile", MediaType.valueOf("application/x-jackson-smile"));
|
||||
MediaType smileMediaType = new MediaType("application", "x-jackson-smile");
|
||||
if (supportedMediaTypes.contains(smileMediaType)) {
|
||||
map.put("smile", smileMediaType);
|
||||
}
|
||||
if (jacksonCborPresent || jackson2CborPresent || kotlinSerializationCborPresent) {
|
||||
if (supportedMediaTypes.contains(MediaType.APPLICATION_CBOR)) {
|
||||
map.put("cbor", MediaType.APPLICATION_CBOR);
|
||||
}
|
||||
if (jacksonYamlPresent || jackson2YamlPresent) {
|
||||
if (supportedMediaTypes.contains(MediaType.APPLICATION_ATOM_XML)) {
|
||||
map.put("yaml", MediaType.APPLICATION_YAML);
|
||||
}
|
||||
return map;
|
||||
|
|
|
@ -91,7 +91,6 @@ import org.springframework.web.util.UrlPathHelper;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
|
||||
/**
|
||||
* A test fixture with a subclass of {@link WebMvcConfigurationSupport} that also
|
||||
|
@ -276,9 +275,6 @@ class WebMvcConfigurationSupportExtensionTests {
|
|||
ContentNegotiationManager manager = mapping.getContentNegotiationManager();
|
||||
assertThat(manager.resolveMediaTypes(webRequest)).isEqualTo(Collections.singletonList(APPLICATION_JSON));
|
||||
|
||||
request.setParameter("f", "xml");
|
||||
assertThat(manager.resolveMediaTypes(webRequest)).isEqualTo(Collections.singletonList(APPLICATION_XML));
|
||||
|
||||
SimpleUrlHandlerMapping handlerMapping = (SimpleUrlHandlerMapping) this.config.resourceHandlerMapping(
|
||||
this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(),
|
||||
this.config.mvcResourceUrlProvider());
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
|
|||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter;
|
||||
|
@ -53,6 +54,7 @@ import org.springframework.util.AntPathMatcher;
|
|||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
@ -213,6 +215,23 @@ class WebMvcConfigurationSupportTests {
|
|||
assertThat(bodyAdvice.get(3).getClass()).isEqualTo(KotlinResponseBodyAdvice.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void contentNegotiationManager() {
|
||||
ApplicationContext context = initContext(WebConfig.class);
|
||||
ContentNegotiationManager contentNegotiation = context.getBean(ContentNegotiationManager.class);
|
||||
Map<String, MediaType> mediaTypeMappings = contentNegotiation.getMediaTypeMappings();
|
||||
|
||||
assertThat(mediaTypeMappings)
|
||||
.containsEntry("atom", MediaType.APPLICATION_ATOM_XML)
|
||||
.containsEntry("rss", MediaType.APPLICATION_RSS_XML)
|
||||
.containsEntry("rss", MediaType.APPLICATION_RSS_XML)
|
||||
.containsEntry("xml", MediaType.APPLICATION_XML)
|
||||
.containsEntry("json", MediaType.APPLICATION_JSON)
|
||||
.containsEntry("smile", MediaType.valueOf("application/x-jackson-smile"))
|
||||
.containsEntry("cbor", MediaType.APPLICATION_CBOR)
|
||||
.containsEntry("yaml", MediaType.APPLICATION_YAML);
|
||||
}
|
||||
|
||||
@Test
|
||||
void uriComponentsContributor() {
|
||||
ApplicationContext context = initContext(WebConfig.class);
|
||||
|
|
Loading…
Reference in New Issue