diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java index 2a42e7623f4..3861dc3ab72 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java @@ -237,6 +237,16 @@ public class ContentNegotiationManagerFactoryBean this.defaultNegotiationStrategy = new FixedContentNegotiationStrategy(contentType); } + /** + * Set the default content types to use when no content type is requested. + *

By default this is not set. + * @see #setDefaultContentTypeStrategy + * @since 5.0 + */ + public void setDefaultContentTypes(List contentTypes) { + this.defaultNegotiationStrategy = new FixedContentNegotiationStrategy(contentTypes); + } + /** * Set a custom {@link ContentNegotiationStrategy} to use to determine * the content type to use when no content type is requested. diff --git a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java index 50478683b13..6c913f651b9 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.http.MediaType; +import org.springframework.util.Assert; import org.springframework.web.context.request.NativeWebRequest; /** @@ -35,23 +36,43 @@ public class FixedContentNegotiationStrategy implements ContentNegotiationStrate private static final Log logger = LogFactory.getLog(FixedContentNegotiationStrategy.class); - private final List contentType; + private final List contentTypes; /** - * Create an instance with the given content type. + * Constructor with a single default {@code MediaType}. */ public FixedContentNegotiationStrategy(MediaType contentType) { - this.contentType = Collections.singletonList(contentType); + this(Collections.singletonList(contentType)); + } + + /** + * Constructor with an ordered List of default {@code MediaType}'s to return + * for use in applications that support a variety of content types. + *

Consider appending {@link MediaType#ALL} at the end if destinations + * are present which do not support any of the other default media types. + * @since 5.0 + */ + public FixedContentNegotiationStrategy(List contentTypes) { + Assert.notNull(contentTypes, "'contentTypes' must not be null"); + this.contentTypes = Collections.unmodifiableList(contentTypes); + } + + + /** + * Return the configured list of media types. + */ + public List getContentTypes() { + return this.contentTypes; } @Override public List resolveMediaTypes(NativeWebRequest request) { if (logger.isDebugEnabled()) { - logger.debug("Requested media types: " + this.contentType); + logger.debug("Requested media types: " + this.contentTypes); } - return this.contentType; + return this.contentTypes; } } diff --git a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java index 5041ed05cbd..10a83ffab83 100644 --- a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java +++ b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java @@ -16,13 +16,14 @@ package org.springframework.web.accept; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; - import org.springframework.http.MediaType; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockServletContext; @@ -168,13 +169,24 @@ public class ContentNegotiationManagerFactoryBeanTests { this.factoryBean.afterPropertiesSet(); ContentNegotiationManager manager = this.factoryBean.getObject(); - assertEquals(Collections.singletonList(MediaType.APPLICATION_JSON), - manager.resolveMediaTypes(this.webRequest)); + assertEquals(MediaType.APPLICATION_JSON, manager.resolveMediaTypes(this.webRequest).get(0)); // SPR-10513 this.servletRequest.addHeader("Accept", MediaType.ALL_VALUE); - assertEquals(Collections.singletonList(MediaType.APPLICATION_JSON), - manager.resolveMediaTypes(this.webRequest)); + assertEquals(MediaType.APPLICATION_JSON, manager.resolveMediaTypes(this.webRequest).get(0)); + } + + @Test // SPR-15367 + public void setDefaultContentTypes() throws Exception { + List mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON, MediaType.ALL); + this.factoryBean.setDefaultContentTypes(mediaTypes); + this.factoryBean.afterPropertiesSet(); + ContentNegotiationManager manager = this.factoryBean.getObject(); + + assertEquals(mediaTypes, manager.resolveMediaTypes(this.webRequest)); + + this.servletRequest.addHeader("Accept", MediaType.ALL_VALUE); + assertEquals(mediaTypes, manager.resolveMediaTypes(this.webRequest)); } @Test // SPR-12286 diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/FixedContentTypeResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/FixedContentTypeResolver.java index 9f8b8e2ea9a..7f6795d2669 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/FixedContentTypeResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/FixedContentTypeResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,30 +19,58 @@ package org.springframework.web.reactive.accept; import java.util.Collections; import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.http.MediaType; import org.springframework.web.server.ServerWebExchange; /** - * A {@link RequestedContentTypeResolver} that resolves to a fixed list of media types. + * {@code RequestedContentTypeResolver} with a fixed list of media types. * * @author Rossen Stoyanchev * @since 5.0 */ public class FixedContentTypeResolver implements RequestedContentTypeResolver { + private static final Log logger = LogFactory.getLog(FixedContentTypeResolver.class); + + private final List mediaTypes; /** - * Create an instance with the given content type. + * Constructor with a single default {@code MediaType}. */ - public FixedContentTypeResolver(MediaType mediaTypes) { - this.mediaTypes = Collections.singletonList(mediaTypes); + public FixedContentTypeResolver(MediaType mediaType) { + this(Collections.singletonList(mediaType)); } + /** + * Constructor with an ordered List of default {@code MediaType}'s to return + * for use in applications that support a variety of content types. + *

Consider appending {@link MediaType#ALL} at the end if destinations + * are present which do not support any of the other default media types. + */ + public FixedContentTypeResolver(List mediaTypes) { + this.mediaTypes = Collections.unmodifiableList(mediaTypes); + } + + + /** + * Return the configured list of media types. + */ + public List getContentTypes() { + return this.mediaTypes; + } + + @Override public List resolveMediaTypes(ServerWebExchange exchange) { + if (logger.isDebugEnabled()) { + logger.debug("Requested media types: " + this.mediaTypes); + } return this.mediaTypes; } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java index 893ed225e8d..264ce1fa290 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java @@ -16,6 +16,7 @@ package org.springframework.web.reactive.accept; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -198,12 +199,18 @@ public class RequestedContentTypeResolverBuilder { } /** - * Set the default content type to use when no content type is requested. + * Set the default content type(s) to use when no content type is requested + * in order of priority. + * + *

If destinations are present that do not support any of the given media + * types, consider appending {@link MediaType#ALL} at the end. + * *

By default this is not set. + * * @see #defaultContentTypeResolver */ - public RequestedContentTypeResolverBuilder defaultContentType(MediaType contentType) { - this.contentTypeResolver = new FixedContentTypeResolver(contentType); + public RequestedContentTypeResolverBuilder defaultContentType(MediaType... contentTypes) { + this.contentTypeResolver = new FixedContentTypeResolver(Arrays.asList(contentTypes)); return this; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java index c085e62a297..8b87f07acce 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java @@ -15,8 +15,10 @@ */ package org.springframework.web.servlet.config.annotation; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; + import javax.servlet.ServletContext; import org.springframework.http.MediaType; @@ -217,12 +219,18 @@ public class ContentNegotiationConfigurer { } /** - * Set the default content type to use when no content type is requested. + * Set the default content type(s) to use when no content type is requested + * in order of priority. + * + *

If destinations are present that do not support any of the given media + * types, consider appending {@link MediaType#ALL} at the end. + * *

By default this is not set. + * * @see #defaultContentTypeStrategy */ - public ContentNegotiationConfigurer defaultContentType(MediaType defaultContentType) { - this.factory.setDefaultContentType(defaultContentType); + public ContentNegotiationConfigurer defaultContentType(MediaType... defaultContentTypes) { + this.factory.setDefaultContentTypes(Arrays.asList(defaultContentTypes)); return this; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurerTests.java index 902c09af894..7c42ceed118 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import org.springframework.web.accept.FixedContentNegotiationStrategy; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; /** * Test fixture for {@link ContentNegotiationConfigurer} tests. @@ -56,7 +56,7 @@ public class ContentNegotiationConfigurerTests { this.servletRequest.setRequestURI("/flower.gif"); assertEquals("Should be able to resolve file extensions by default", - Arrays.asList(MediaType.IMAGE_GIF), manager.resolveMediaTypes(this.webRequest)); + MediaType.IMAGE_GIF, manager.resolveMediaTypes(this.webRequest).get(0)); this.servletRequest.setRequestURI("/flower?format=gif"); this.servletRequest.addParameter("format", "gif"); @@ -68,7 +68,7 @@ public class ContentNegotiationConfigurerTests { this.servletRequest.addHeader("Accept", MediaType.IMAGE_GIF_VALUE); assertEquals("Should resolve Accept header by default", - Arrays.asList(MediaType.IMAGE_GIF), manager.resolveMediaTypes(this.webRequest)); + MediaType.IMAGE_GIF, manager.resolveMediaTypes(this.webRequest).get(0)); } @Test @@ -77,7 +77,7 @@ public class ContentNegotiationConfigurerTests { ContentNegotiationManager manager = this.configurer.getContentNegotiationManager(); this.servletRequest.setRequestURI("/flower.json"); - assertEquals(Arrays.asList(MediaType.APPLICATION_JSON), manager.resolveMediaTypes(this.webRequest)); + assertEquals(MediaType.APPLICATION_JSON, manager.resolveMediaTypes(this.webRequest).get(0)); } @Test @@ -90,7 +90,7 @@ public class ContentNegotiationConfigurerTests { this.servletRequest.setRequestURI("/flower"); this.servletRequest.addParameter("f", "json"); - assertEquals(Arrays.asList(MediaType.APPLICATION_JSON), manager.resolveMediaTypes(this.webRequest)); + assertEquals(MediaType.APPLICATION_JSON, manager.resolveMediaTypes(this.webRequest).get(0)); } @Test @@ -109,7 +109,15 @@ public class ContentNegotiationConfigurerTests { this.configurer.defaultContentType(MediaType.APPLICATION_JSON); ContentNegotiationManager manager = this.configurer.getContentNegotiationManager(); - assertEquals(Arrays.asList(MediaType.APPLICATION_JSON), manager.resolveMediaTypes(this.webRequest)); + assertEquals(MediaType.APPLICATION_JSON, manager.resolveMediaTypes(this.webRequest).get(0)); + } + + @Test + public void setMultipleDefaultContentTypes() throws Exception { + this.configurer.defaultContentType(MediaType.APPLICATION_JSON, MediaType.ALL); + ContentNegotiationManager manager = this.configurer.getContentNegotiationManager(); + + assertEquals(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.ALL), manager.resolveMediaTypes(this.webRequest)); } @Test @@ -117,6 +125,6 @@ public class ContentNegotiationConfigurerTests { this.configurer.defaultContentTypeStrategy(new FixedContentNegotiationStrategy(MediaType.APPLICATION_JSON)); ContentNegotiationManager manager = this.configurer.getContentNegotiationManager(); - assertEquals(Arrays.asList(MediaType.APPLICATION_JSON), manager.resolveMediaTypes(this.webRequest)); + assertEquals(MediaType.APPLICATION_JSON, manager.resolveMediaTypes(this.webRequest).get(0)); } }