diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java index b7ae373235..bb7cc67e01 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Map; import javax.servlet.Servlet; @@ -54,6 +55,7 @@ import org.springframework.core.io.Resource; import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; import org.springframework.format.datetime.DateFormatter; +import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.validation.DefaultMessageCodesResolver; import org.springframework.validation.MessageCodesResolver; @@ -68,6 +70,7 @@ import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; +import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceChainRegistration; @@ -155,6 +158,14 @@ public class WebMvcAutoConfiguration { } } + @Override + public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + Map mediaTypes = this.mvcProperties.getMediaTypes(); + for (String extension : mediaTypes.keySet()) { + configurer.mediaType(extension, mediaTypes.get(extension)); + } + } + @Bean @ConditionalOnMissingBean public InternalResourceViewResolver defaultViewResolver() { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java index 92022d076e..b887aba20b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcProperties.java @@ -16,10 +16,13 @@ package org.springframework.boot.autoconfigure.web; +import java.util.LinkedHashMap; import java.util.Locale; +import java.util.Map; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.http.MediaType; import org.springframework.validation.DefaultMessageCodesResolver; /** @@ -63,6 +66,11 @@ public class WebMvcProperties { private final View view = new View(); + /** + * Maps file extensions to media types for content negotiation, e.g. yml->text/yaml. + */ + private Map mediaTypes = new LinkedHashMap(); + public DefaultMessageCodesResolver.Format getMessageCodesResolverFormat() { return this.messageCodesResolverFormat; } @@ -113,6 +121,14 @@ public class WebMvcProperties { return this.view; } + public Map getMediaTypes() { + return this.mediaTypes; + } + + public void setMediaTypes(Map mediaTypes) { + this.mediaTypes = mediaTypes; + } + public static class Async { /** diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java index 59e8178079..f8bbbbee19 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java @@ -53,6 +53,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; +import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.filter.HttpPutFormContentFilter; import org.springframework.web.servlet.HandlerAdapter; @@ -90,6 +91,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; /** * Tests for {@link WebMvcAutoConfiguration}. @@ -430,6 +432,16 @@ public class WebMvcAutoConfigurationTests { assertEquals(123456L, actual); } + @Test + public void customMediaTypes() throws Exception { + load("spring.mvc.mediaTypes.yaml:text/yaml"); + RequestMappingHandlerAdapter adapter = this.context + .getBean(RequestMappingHandlerAdapter.class); + ContentNegotiationManager actual = (ContentNegotiationManager) ReflectionTestUtils + .getField(adapter, "contentNegotiationManager"); + assertTrue(actual.getAllFileExtensions().contains("yaml")); + } + @Test public void httpPutFormContentFilterIsAutoConfigured() { load();