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 289b7b3e711..ec62ae516ec 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 @@ -33,13 +33,51 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.context.ServletContextAware; /** - * A factory providing convenient access to a {@code ContentNegotiationManager} - * configured with one or more {@link ContentNegotiationStrategy} instances. + * Factory to create a {@code ContentNegotiationManager} and configure it with + * one or more {@link ContentNegotiationStrategy} instances via simple setters. + * The following table shows setters, resulting strategy instances, and if in + * use by default: * - *

By default strategies for checking the extension of the request path and - * the {@code Accept} header are registered. The path extension check will perform - * lookups through the {@link ServletContext} and the Java Activation Framework - * (if present) unless {@linkplain #setMediaTypes media types} are configured. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #setFavorPathExtension favorPathExtension}{@link PathExtensionContentNegotiationStrategy}Yes
{@link #setFavorParameter favorParameter}{@link ParameterContentNegotiationStrategy}-
{@link #setIgnoreAcceptHeader ignoreAcceptHeader}{@link HeaderContentNegotiationStrategy}Yes
{@link #setDefaultContentType defaultContentType}{@link FixedContentNegotiationStrategy}-
{@link #setDefaultContentTypeStrategy defaultContentTypeStrategy}{@link ContentNegotiationStrategy}-
+ * + *

The order in which strategies are configured is fixed. Setters may only + * turn individual strategies on or off. If you need a custom order for any + * reason simply instantiate {@code ContentNegotiationManager} directly. + * + *

For the path extension and parameter strategies you may explicitly add + * {@link #setMediaTypes MediaType mappings}. This will be used to resolve path + * extensions or a parameter value such as "json" to a media type such as + * "application/json". + * + *

The path extension strategy will also use {@link ServletContext#getMimeType} + * and the Java Activation framework (JAF), if available, to resolve a path + * extension to a MediaType. You may {@link #setUseJaf suppress} the use of JAF. * * @author Rossen Stoyanchev * @since 3.2 @@ -69,21 +107,23 @@ public class ContentNegotiationManagerFactoryBean /** - * Indicate whether the extension of the request path should be used to determine - * the requested media type with the highest priority. - *

By default this value is set to {@code true} in which case a request + * Whether the path extension in the URL path should be used to determine + * the requested media type. + *

By default this is set to {@code true} in which case a request * for {@code /hotels.pdf} will be interpreted as a request for - * {@code "application/pdf"} regardless of the {@code Accept} header. + * {@code "application/pdf"} regardless of the 'Accept' header. */ public void setFavorPathExtension(boolean favorPathExtension) { this.favorPathExtension = favorPathExtension; } /** - * Add mappings from file extensions to media types represented as strings. - *

When this mapping is not set or when an extension is not found, the Java - * Action Framework, if available, may be used if enabled via - * {@link #setFavorPathExtension(boolean)}. + * Add mappings from keys, extracted from a path extension or a query + * parameter, to a MediaType. This is required in order for the parameter + * strategy to work. The path extension strategy will also try + * {@link ServletContext#getMimeType} and JAF if it is present and is not + * suppressed via {@link #setUseJaf}. + * @param mediaTypes media type mappings * @see #addMediaType(String, MediaType) * @see #addMediaTypes(Map) */ @@ -91,26 +131,25 @@ public class ContentNegotiationManagerFactoryBean if (!CollectionUtils.isEmpty(mediaTypes)) { for (Entry entry : mediaTypes.entrySet()) { String extension = ((String)entry.getKey()).toLowerCase(Locale.ENGLISH); - this.mediaTypes.put(extension, MediaType.valueOf((String) entry.getValue())); + MediaType mediaType = MediaType.valueOf((String) entry.getValue()); + this.mediaTypes.put(extension, mediaType); } } } /** - * Add a mapping from a file extension to a media type. - *

If no mapping is added or when an extension is not found, the Java - * Action Framework, if available, may be used if enabled via - * {@link #setFavorPathExtension(boolean)}. + * An alternative to {@link #setMediaTypes} for use in Java code. + * @see #setMediaTypes + * @see #addMediaTypes */ public void addMediaType(String fileExtension, MediaType mediaType) { this.mediaTypes.put(fileExtension, mediaType); } /** - * Add mappings from file extensions to media types. - *

If no mappings are added or when an extension is not found, the Java - * Action Framework, if available, may be used if enabled via - * {@link #setFavorPathExtension(boolean)}. + * An alternative to {@link #setMediaTypes} for use in Java code. + * @see #setMediaTypes + * @see #addMediaType */ public void addMediaTypes(Map mediaTypes) { if (mediaTypes != null) { @@ -119,37 +158,31 @@ public class ContentNegotiationManagerFactoryBean } /** - * Whether to ignore requests that have a file extension that does not match - * any mapped media types. Setting this to {@code false} will result in a - * {@code HttpMediaTypeNotAcceptableException} when there is no match. - * + * Whether to ignore requests with path extension that cannot be resolved + * to any media type. Setting this to {@code false} will result in an + * {@code HttpMediaTypeNotAcceptableException} if there is no match. *

By default this is set to {@code true}. */ - public void setIgnoreUnknownPathExtensions(boolean ignoreUnknownPathExtensions) { - this.ignoreUnknownPathExtensions = ignoreUnknownPathExtensions; + public void setIgnoreUnknownPathExtensions(boolean ignore) { + this.ignoreUnknownPathExtensions = ignore; } /** - * Whether to use the Java Activation Framework as a fallback option - * to map from file extensions to media types. This is used only when - * {@link #setFavorPathExtension(boolean)} is set to {@code true}. - *

The default value is {@code true}. - * @see #setParameterName - * @see #setMediaTypes + * When {@link #setFavorPathExtension favorPathExtension} is set, this + * property determines whether to allow use of JAF (Java Activation Framework) + * to resolve a path extension to a specific MediaType. + *

By default this is not set in which case + * {@code PathExtensionContentNegotiationStrategy} will use JAF if available. */ public void setUseJaf(boolean useJaf) { this.useJaf = useJaf; } /** - * Indicate whether a request parameter should be used to determine the - * requested media type with the 2nd highest priority, i.e. - * after path extensions but before the {@code Accept} header. - *

The default value is {@code false}. If set to to {@code true}, a request - * for {@code /hotels?format=pdf} will be interpreted as a request for - * {@code "application/pdf"} regardless of the {@code Accept} header. - *

To use this option effectively you must also configure the MediaType - * type mappings via {@link #setMediaTypes(Properties)}. + * Whether a request parameter ("format" by default) should be used to + * determine the requested media type. For this option to work you must + * register {@link #setMediaTypes media type mappings}. + *

By default this is set to {@code false}. * @see #setParameterName */ public void setFavorParameter(boolean favorParameter) { @@ -157,8 +190,7 @@ public class ContentNegotiationManagerFactoryBean } /** - * Set the parameter name that can be used to determine the requested media type - * if the {@link #setFavorParameter} property is {@code true}. + * Set the query parameter name to use when {@link #setFavorParameter} is on. *

The default parameter name is {@code "format"}. */ public void setParameterName(String parameterName) { @@ -167,10 +199,7 @@ public class ContentNegotiationManagerFactoryBean } /** - * Indicate whether the HTTP {@code Accept} header should be ignored altogether. - * If set the {@code Accept} header is checked at the - * 3rd highest priority, i.e. after the request path extension and - * possibly a request parameter if configured. + * Whether to disable checking the 'Accept' request header. *

By default this value is set to {@code false}. */ public void setIgnoreAcceptHeader(boolean ignoreAcceptHeader) { @@ -178,27 +207,27 @@ public class ContentNegotiationManagerFactoryBean } /** - * Set the default content type to use when no content type was requested. - *

Note that internally this method creates and adds a - * {@link org.springframework.web.accept.FixedContentNegotiationStrategy - * FixedContentNegotiationStrategy}. Alternatively you can also provide a - * custom strategy via {@link #setDefaultContentTypeStrategy}. + * Set the default content type to use when no content type is requested. + *

By default this is not set. + * @see #setDefaultContentTypeStrategy */ public void setDefaultContentType(MediaType contentType) { this.defaultNegotiationStrategy = new FixedContentNegotiationStrategy(contentType); } /** - * Configure a custom {@link ContentNegotiationStrategy} to use to determine - * the default content type to use when no content type was requested. - *

However also consider using {@link #setDefaultContentType} which - * provides a simpler alternative to doing the same. + * Set a custom {@link ContentNegotiationStrategy} to use to determine + * the content type to use when no content type is requested. + * @see #setDefaultContentType * @since 4.1.2 */ public void setDefaultContentTypeStrategy(ContentNegotiationStrategy strategy) { this.defaultNegotiationStrategy = strategy; } + /** + * Invoked by Spring to inject the ServletContext. + */ @Override public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; @@ -237,7 +266,7 @@ public class ContentNegotiationManagerFactoryBean } if (this.defaultNegotiationStrategy != null) { - strategies.add(defaultNegotiationStrategy); + strategies.add(this.defaultNegotiationStrategy); } this.contentNegotiationManager = new ContentNegotiationManager(strategies); 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 35a928e2b9f..1c55a3eb2b1 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 @@ -23,21 +23,64 @@ import org.springframework.http.MediaType; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; import org.springframework.web.accept.ContentNegotiationStrategy; +import org.springframework.web.accept.FixedContentNegotiationStrategy; +import org.springframework.web.accept.HeaderContentNegotiationStrategy; +import org.springframework.web.accept.ParameterContentNegotiationStrategy; +import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; /** - * Help to create and configure a {@link ContentNegotiationManager}. + * Creates a {@code ContentNegotiationManager} and configures it with + * one or more {@link ContentNegotiationStrategy} instances. The following shows + * the resulting strategy instances, the methods used to configured them, and + * whether enabled by default: * - *

By default strategies for checking the extension of the request path and - * the {@code Accept} header are registered. The path extension check will perform - * lookups through the {@link ServletContext} and the Java Activation Framework - * (if present) unless {@linkplain #mediaTypes(Map) media types} are configured. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #favorPathExtension}{@link PathExtensionContentNegotiationStrategy}Yes
{@link #favorParameter}{@link ParameterContentNegotiationStrategy}-
{@link #ignoreAcceptHeader}{@link HeaderContentNegotiationStrategy}Yes
{@link #defaultContentType}{@link FixedContentNegotiationStrategy}-
{@link #defaultContentTypeStrategy}{@link ContentNegotiationStrategy}-
+ * + *

The order in which strategies are configured is fixed. You can only turn + * them on or off. + * + *

For the path extension and parameter strategies you may explicitly add + * {@link #mediaType MediaType mappings}. Those will be used to resolve path + * extensions and/or a query parameter value such as "json" to a concrete media + * type such as "application/json". + * + *

The path extension strategy will also use {@link ServletContext#getMimeType} + * and the Java Activation framework (JAF), if available, to resolve a path + * extension to a MediaType. You may however {@link #useJaf suppress} the use + * of JAF. * * @author Rossen Stoyanchev * @since 3.2 */ public class ContentNegotiationConfigurer { - private final ContentNegotiationManagerFactoryBean factoryBean = + private final ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean(); private final Map mediaTypes = new HashMap(); @@ -47,25 +90,32 @@ public class ContentNegotiationConfigurer { * Class constructor with {@link javax.servlet.ServletContext}. */ public ContentNegotiationConfigurer(ServletContext servletContext) { - this.factoryBean.setServletContext(servletContext); + this.factory.setServletContext(servletContext); } + /** - * Indicate whether the extension of the request path should be used to determine - * the requested media type with the highest priority. - *

By default this value is set to {@code true} in which case a request + * Whether the path extension in the URL path should be used to determine + * the requested media type. + *

By default this is set to {@code true} in which case a request * for {@code /hotels.pdf} will be interpreted as a request for - * {@code "application/pdf"} regardless of the {@code Accept} header. + * {@code "application/pdf"} regardless of the 'Accept' header. */ public ContentNegotiationConfigurer favorPathExtension(boolean favorPathExtension) { - this.factoryBean.setFavorPathExtension(favorPathExtension); + this.factory.setFavorPathExtension(favorPathExtension); return this; } /** - * Add mappings from file extensions to media types. - *

If this property is not set, the Java Action Framework, if available, may - * still be used in conjunction with {@link #favorPathExtension(boolean)}. + * Add a mapping from a key, extracted from a path extension or a query + * parameter, to a MediaType. This is required in order for the parameter + * strategy to work. The path extension strategy will also try + * {@link ServletContext#getMimeType} and JAF if it is present and is not + * suppressed via {@link #useJaf}. + * @param extension the key to look up + * @param mediaType the media type + * @see #mediaTypes(Map) + * @see #replaceMediaTypes(Map) */ public ContentNegotiationConfigurer mediaType(String extension, MediaType mediaType) { this.mediaTypes.put(extension, mediaType); @@ -73,9 +123,9 @@ public class ContentNegotiationConfigurer { } /** - * Add mappings from file extensions to media types. - *

If this property is not set, the Java Action Framework, if available, may - * still be used in conjunction with {@link #favorPathExtension(boolean)}. + * An alternative to {@link #mediaType}. + * @see #mediaType(String, MediaType) + * @see #replaceMediaTypes(Map) */ public ContentNegotiationConfigurer mediaTypes(Map mediaTypes) { if (mediaTypes != null) { @@ -85,9 +135,9 @@ public class ContentNegotiationConfigurer { } /** - * Add mappings from file extensions to media types replacing any previous mappings. - *

If this property is not set, the Java Action Framework, if available, may - * still be used in conjunction with {@link #favorPathExtension(boolean)}. + * Similar to {@link #mediaType} but for replacing existing mappings. + * @see #mediaType(String, MediaType) + * @see #mediaTypes(Map) */ public ContentNegotiationConfigurer replaceMediaTypes(Map mediaTypes) { this.mediaTypes.clear(); @@ -96,101 +146,83 @@ public class ContentNegotiationConfigurer { } /** - * Whether to ignore requests that have a file extension that does not match - * any mapped media types. Setting this to {@code false} will result in a - * {@code HttpMediaTypeNotAcceptableException} when there is no match. - * + * Whether to ignore requests with path extension that cannot be resolved + * to any media type. Setting this to {@code false} will result in an + * {@code HttpMediaTypeNotAcceptableException} if there is no match. *

By default this is set to {@code true}. */ public ContentNegotiationConfigurer ignoreUnknownPathExtensions(boolean ignore) { - this.factoryBean.setIgnoreUnknownPathExtensions(ignore); + this.factory.setIgnoreUnknownPathExtensions(ignore); return this; } /** - * Indicate whether to use the Java Activation Framework as a fallback option - * to map from file extensions to media types. This is used only when - * {@link #favorPathExtension(boolean)} is set to {@code true}. - *

The default value is {@code true}. - * @see #parameterName - * @see #mediaTypes(Map) + * When {@link #favorPathExtension} is set, this property determines whether + * to allow use of JAF (Java Activation Framework) to resolve a path + * extension to a specific MediaType. + *

By default this is not set in which case + * {@code PathExtensionContentNegotiationStrategy} will use JAF if available. */ public ContentNegotiationConfigurer useJaf(boolean useJaf) { - this.factoryBean.setUseJaf(useJaf); + this.factory.setUseJaf(useJaf); return this; } /** - * Indicate whether a request parameter should be used to determine the - * requested media type with the 2nd highest priority, i.e. - * after path extensions but before the {@code Accept} header. - *

The default value is {@code false}. If set to to {@code true}, a request - * for {@code /hotels?format=pdf} will be interpreted as a request for - * {@code "application/pdf"} regardless of the {@code Accept} header. - *

To use this option effectively you must also configure the MediaType - * type mappings via {@link #mediaTypes(Map)}. + * Whether a request parameter ("format" by default) should be used to + * determine the requested media type. For this option to work you must + * register {@link #mediaType(String, MediaType) media type mappings}. + *

By default this is set to {@code false}. * @see #parameterName(String) */ public ContentNegotiationConfigurer favorParameter(boolean favorParameter) { - this.factoryBean.setFavorParameter(favorParameter); + this.factory.setFavorParameter(favorParameter); return this; } /** - * Set the parameter name that can be used to determine the requested media type - * if the {@link #favorParameter(boolean)} property is {@code true}. + * Set the query parameter name to use when {@link #favorParameter} is on. *

The default parameter name is {@code "format"}. */ public ContentNegotiationConfigurer parameterName(String parameterName) { - this.factoryBean.setParameterName(parameterName); + this.factory.setParameterName(parameterName); return this; } /** - * Indicate whether the HTTP {@code Accept} header should be ignored altogether. - * If set the {@code Accept} header is checked at the - * 3rd highest priority, i.e. after the request path extension and - * possibly a request parameter if configured. + * Whether to disable checking the 'Accept' request header. *

By default this value is set to {@code false}. */ public ContentNegotiationConfigurer ignoreAcceptHeader(boolean ignoreAcceptHeader) { - this.factoryBean.setIgnoreAcceptHeader(ignoreAcceptHeader); + this.factory.setIgnoreAcceptHeader(ignoreAcceptHeader); return this; } /** - * Set the default content type to use when no content type was requested. - *

Note that internally this method creates and adds a - * {@link org.springframework.web.accept.FixedContentNegotiationStrategy - * FixedContentNegotiationStrategy}. Alternatively you can also provide a - * custom strategy via {@link #defaultContentTypeStrategy}. + * Set the default content type to use when no content type is requested. + *

By default this is not set. + * @see #defaultContentTypeStrategy */ public ContentNegotiationConfigurer defaultContentType(MediaType defaultContentType) { - this.factoryBean.setDefaultContentType(defaultContentType); + this.factory.setDefaultContentType(defaultContentType); return this; } /** - * Configure a custom {@link ContentNegotiationStrategy} to use to determine - * the default content type to use when no content type was requested. - *

However also consider using {@link #defaultContentType} which provides - * a simpler alternative to doing the same. + * Set a custom {@link ContentNegotiationStrategy} to use to determine + * the content type to use when no content type is requested. + * @see #defaultContentType * @since 4.1.2 */ public ContentNegotiationConfigurer defaultContentTypeStrategy(ContentNegotiationStrategy defaultStrategy) { - this.factoryBean.setDefaultContentTypeStrategy(defaultStrategy); + this.factory.setDefaultContentTypeStrategy(defaultStrategy); return this; } - /** - * Return the configured {@link ContentNegotiationManager} instance - */ protected ContentNegotiationManager getContentNegotiationManager() throws Exception { - if (!this.mediaTypes.isEmpty()) { - this.factoryBean.addMediaTypes(mediaTypes); - } - this.factoryBean.afterPropertiesSet(); - return this.factoryBean.getObject(); + this.factory.addMediaTypes(this.mediaTypes); + this.factory.afterPropertiesSet(); + return this.factory.getObject(); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java index 8203ea201ca..35fa13e3172 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -20,8 +20,8 @@ import org.springframework.util.PathMatcher; import org.springframework.web.util.UrlPathHelper; /** - * Helps with configuring HandlerMappings path matching options such as trailing slash match, - * suffix registration, path matcher and path helper. + * Helps with configuring HandlerMappings path matching options such as trailing + * slash match, suffix registration, path matcher and path helper. * *

Configured path matcher and path helper instances are shared for: *