Introduce 'useRegisteredExtensionsOnly' property in classes using MediaTypeFactory

This commit introduces a `useRegisteredExtensionsOnly` property that
indicates whether classes that use the `MediaTypeFactory` for supplying
default media types can do so.

 - In classes that were introduced in Spring 5.0, the
 `useRegisteredExtensionsOnly` property takes the place of the
 `useJaf` property that was removed in 0aaa652
 - In classes that existed before Spring 5.0, the
 `useRegisteredExtensionsOnly` property is added in addition to the
 deprecated `useJaf`, the latter delegating to the former, but with
 flipped behavior.

Issue: SPR-14908
This commit is contained in:
Arjen Poutsma
2017-03-23 14:56:43 +01:00
parent e2aa880301
commit a287e67992
8 changed files with 101 additions and 22 deletions

View File

@@ -104,11 +104,9 @@ public class MediaTypeFactory {
* @return the corresponding media type, or {@code null} if none found
*/
public static Optional<MediaType> getMediaType(Resource resource) {
if (resource == null) {
return Optional.empty();
}
String filename = resource.getFilename();
return (filename != null ? getMediaType(filename) : Optional.empty());
return Optional.ofNullable(resource)
.map(Resource::getFilename)
.flatMap(MediaTypeFactory::getMediaType);
}
/**
@@ -117,8 +115,7 @@ public class MediaTypeFactory {
* @return the corresponding media type, or {@code null} if none found
*/
public static Optional<MediaType> getMediaType(String filename) {
List<MediaType> mediaTypes = getMediaTypes(filename);
return (!mediaTypes.isEmpty() ? Optional.of(mediaTypes.get(0)) : Optional.empty());
return getMediaTypes(filename).stream().findFirst();
}
/**

View File

@@ -143,7 +143,9 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy, Me
* <p>At startup this method returns extensions explicitly registered with
* either {@link PathExtensionContentNegotiationStrategy} or
* {@link ParameterContentNegotiationStrategy}. At runtime if there is a
* "path extension" strategy, the list of extensions may
* "path extension" strategy and its
* {@link PathExtensionContentNegotiationStrategy#setUseRegisteredExtensionsOnly(boolean)
* useRegisteredExtensionsOnly} property is set to "false", the list of extensions may
* increase as file extensions are resolved via
* {@link org.springframework.http.MediaTypeFactory} and cached.
*/

View File

@@ -100,6 +100,8 @@ public class ContentNegotiationManagerFactoryBean
private boolean ignoreUnknownPathExtensions = true;
private Boolean useRegisteredExtensionsOnly;
private String parameterName = "format";
private ContentNegotiationStrategy defaultNegotiationStrategy;
@@ -175,10 +177,27 @@ public class ContentNegotiationManagerFactoryBean
}
/**
* @deprecated as 5.0, in favor of {@link MediaTypeFactory}, which has no JAF dependency.
* @deprecated as of 5.0, in favor of {@link #setUseRegisteredExtensionsOnly(boolean)}, which
* has reverse behavior.
*/
@Deprecated
public void setUseJaf(boolean useJaf) {
setUseRegisteredExtensionsOnly(!useJaf);
}
/**
* When {@link #setFavorPathExtension favorPathExtension} is set, this
* property determines whether to use only registered {@code MediaType} mappings
* to resolve a path extension to a specific MediaType.
* <p>By default this is not set in which case
* {@code PathExtensionContentNegotiationStrategy} will use defaults if available.
*/
public void setUseRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
}
private boolean useRegisteredExtensionsOnly() {
return (this.useRegisteredExtensionsOnly != null && this.useRegisteredExtensionsOnly);
}
/**
@@ -244,7 +263,7 @@ public class ContentNegotiationManagerFactoryBean
if (this.favorPathExtension) {
PathExtensionContentNegotiationStrategy strategy;
if (this.servletContext != null) {
if (this.servletContext != null && !useRegisteredExtensionsOnly()) {
strategy = new ServletPathExtensionContentNegotiationStrategy(
this.servletContext, this.mediaTypes);
}
@@ -252,6 +271,9 @@ public class ContentNegotiationManagerFactoryBean
strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes);
}
strategy.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions);
if (this.useRegisteredExtensionsOnly != null) {
strategy.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly);
}
strategies.add(strategy);
}

View File

@@ -51,6 +51,8 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private boolean useRegisteredExtensionsOnly = false;
private boolean ignoreUnknownExtensions = true;
@@ -81,10 +83,20 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
}
/**
* @deprecated as 5.0, in favor of {@link MediaTypeFactory}, which has no JAF dependency.
* @deprecated as of 5.0, in favor of {@link #setUseRegisteredExtensionsOnly(boolean)}.
*/
@Deprecated
public void setUseJaf(boolean useJaf) {
setUseRegisteredExtensionsOnly(!useJaf);
}
/**
* Whether to only use the registered mappings to look up file extensions, or also refer to
* defaults.
* <p>By default this is set to {@code false}, meaning that defaults are used.
*/
public void setUseRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
}
/**
@@ -113,9 +125,11 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
throws HttpMediaTypeNotAcceptableException {
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + extension);
if (mediaType.isPresent()) {
return mediaType.get();
if (!this.useRegisteredExtensionsOnly) {
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + extension);
if (mediaType.isPresent()) {
return mediaType.get();
}
}
if (this.ignoreUnknownExtensions) {
return null;

View File

@@ -93,6 +93,7 @@ public class ServletPathExtensionContentNegotiationStrategy extends PathExtensio
* @return the MediaType for the extension or {@code null}.
* @since 4.3
*/
@Override
public MediaType getMediaTypeForResource(Resource resource) {
MediaType mediaType = null;
if (this.servletContext != null) {

View File

@@ -42,6 +42,8 @@ import org.springframework.web.util.UriUtils;
*/
public class PathExtensionContentTypeResolver extends AbstractMappingContentTypeResolver {
private boolean useRegisteredExtensionsOnly = false;
private boolean ignoreUnknownExtensions = true;
@@ -61,6 +63,15 @@ public class PathExtensionContentTypeResolver extends AbstractMappingContentType
}
/**
* Whether to only use the registered mappings to look up file extensions, or also refer to
* defaults.
* <p>By default this is set to {@code false}, meaning that defaults are used.
*/
public void setUseRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
}
/**
* Whether to ignore requests with unknown file extension. Setting this to
* {@code false} results in {@code HttpMediaTypeNotAcceptableException}.
@@ -80,14 +91,16 @@ public class PathExtensionContentTypeResolver extends AbstractMappingContentType
@Override
protected MediaType handleNoMatch(String key) throws NotAcceptableStatusException {
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + key);
if (mediaType.isPresent()) {
return mediaType.get();
if (!this.useRegisteredExtensionsOnly) {
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + key);
if (mediaType.isPresent()) {
return mediaType.get();
}
}
if (!this.ignoreUnknownExtensions) {
throw new NotAcceptableStatusException(getAllMediaTypes());
if (this.ignoreUnknownExtensions) {
return null;
}
return null;
throw new NotAcceptableStatusException(getAllMediaTypes());
}
/**

View File

@@ -93,6 +93,8 @@ public class RequestedContentTypeResolverBuilder {
private boolean ignoreUnknownPathExtensions = true;
private Boolean useRegisteredExtensionsOnly;
private String parameterName = "format";
private RequestedContentTypeResolver contentTypeResolver;
@@ -152,6 +154,18 @@ public class RequestedContentTypeResolverBuilder {
return this;
}
/**
* When {@link #favorPathExtension favorPathExtension} is set, this
* property determines whether to use only registered {@code MediaType} mappings
* to resolve a path extension to a specific MediaType.
* <p>By default this is not set in which case
* {@code PathExtensionContentNegotiationStrategy} will use defaults if available.
*/
public RequestedContentTypeResolverBuilder useRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
return this;
}
/**
* Whether a request parameter ("format" by default) should be used to
* determine the requested media type. For this option to work you must
@@ -211,6 +225,9 @@ public class RequestedContentTypeResolverBuilder {
if (this.favorPathExtension) {
PathExtensionContentTypeResolver resolver = new PathExtensionContentTypeResolver(this.mediaTypes);
resolver.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions);
if (this.useRegisteredExtensionsOnly != null) {
resolver.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly);
}
resolvers.add(resolver);
}

View File

@@ -120,7 +120,7 @@ public class ContentNegotiationConfigurer {
* attack protection).
* <p>The path extension strategy will also try to use
* {@link ServletContext#getMimeType} and {@link MediaTypeFactory} to resolve path
* extensions.
* extensions. To change this behavior see the {@link #useRegisteredExtensionsOnly} property.
* @param extension the key to look up
* @param mediaType the media type
* @see #mediaTypes(Map)
@@ -166,10 +166,23 @@ public class ContentNegotiationConfigurer {
}
/**
* @deprecated as 5.0, in favor of {@link MediaTypeFactory}, which has no JAF dependency.
* @deprecated as of 5.0, in favor of {@link #useRegisteredExtensionsOnly(boolean)}, which
* has reverse behavior.
*/
@Deprecated
public ContentNegotiationConfigurer useJaf(boolean useJaf) {
return this.useRegisteredExtensionsOnly(!useJaf);
}
/**
* When {@link #favorPathExtension favorPathExtension} is set, this
* property determines whether to use only registered {@code MediaType} mappings
* to resolve a path extension to a specific MediaType.
* <p>By default this is not set in which case
* {@code PathExtensionContentNegotiationStrategy} will use defaults if available.
*/
public ContentNegotiationConfigurer useRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
this.factory.setUseRegisteredExtensionsOnly(useRegisteredExtensionsOnly);
return this;
}