This commit is contained in:
Rossen Stoyanchev
2015-09-30 14:04:38 -04:00
parent 39726740e5
commit 42e5ad4111
5 changed files with 225 additions and 176 deletions

View File

@@ -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:
*
* <p>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.
* <table>
* <tr>
* <td>{@link #setFavorPathExtension favorPathExtension}</td>
* <td>{@link PathExtensionContentNegotiationStrategy}</td>
* <td>Yes</td>
* </tr>
* <tr>
* <td>{@link #setFavorParameter favorParameter}</td>
* <td>{@link ParameterContentNegotiationStrategy}</td>
* <td>-</td>
* </tr>
* <tr>
* <td>{@link #setIgnoreAcceptHeader ignoreAcceptHeader}</td>
* <td>{@link HeaderContentNegotiationStrategy}</td>
* <td>Yes</td>
* </tr>
* <tr>
* <td>{@link #setDefaultContentType defaultContentType}</td>
* <td>{@link FixedContentNegotiationStrategy}</td>
* <td>-</td>
* </tr>
* <tr>
* <td>{@link #setDefaultContentTypeStrategy defaultContentTypeStrategy}</td>
* <td>{@link ContentNegotiationStrategy}</td>
* <td>-</td>
* </tr>
* </table>
*
* <p>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.
*
* <p>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".
*
* <p>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 <em>highest priority</em>.
* <p>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.
* <p>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.
* <p>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<Object, Object> 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.
* <p>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.
* <p>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<String, MediaType> 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.
* <p>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}.
* <p>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.
* <p>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 <em>2nd highest priority</em>, i.e.
* after path extensions but before the {@code Accept} header.
* <p>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.
* <p>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}.
* <p>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.
* <p>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
* <em>3rd highest priority</em>, i.e. after the request path extension and
* possibly a request parameter if configured.
* Whether to disable checking the 'Accept' request header.
* <p>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.
* <p>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.
* <p>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.
* <p>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);