Configurable UrlPathHelper in PathExtensionContentNegotiationStrategy
This commit also aligns ResourceUrlProvider's and RequestMappingInfo's UrlPathHelper setter/getter signatures. Issue: SPR-14454
This commit is contained in:
parent
e91c1cd5a7
commit
84afc601b8
|
@ -52,41 +52,45 @@ import org.springframework.web.util.WebUtils;
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
public class PathExtensionContentNegotiationStrategy
|
public class PathExtensionContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy {
|
||||||
extends AbstractMappingContentNegotiationStrategy {
|
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class);
|
|
||||||
|
|
||||||
private static final boolean JAF_PRESENT = ClassUtils.isPresent("javax.activation.FileTypeMap",
|
private static final boolean JAF_PRESENT = ClassUtils.isPresent("javax.activation.FileTypeMap",
|
||||||
PathExtensionContentNegotiationStrategy.class.getClassLoader());
|
PathExtensionContentNegotiationStrategy.class.getClassLoader());
|
||||||
|
|
||||||
private static final UrlPathHelper PATH_HELPER = new UrlPathHelper();
|
private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class);
|
||||||
|
|
||||||
static {
|
|
||||||
PATH_HELPER.setUrlDecode(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||||
|
|
||||||
private boolean useJaf = true;
|
private boolean useJaf = true;
|
||||||
|
|
||||||
private boolean ignoreUnknownExtensions = true;
|
private boolean ignoreUnknownExtensions = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an instance with the given map of file extensions and media types.
|
|
||||||
*/
|
|
||||||
public PathExtensionContentNegotiationStrategy(Map<String, MediaType> mediaTypes) {
|
|
||||||
super(mediaTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance without any mappings to start with. Mappings may be added
|
* Create an instance without any mappings to start with. Mappings may be added
|
||||||
* later on if any extensions are resolved through the Java Activation framework.
|
* later on if any extensions are resolved through the Java Activation framework.
|
||||||
*/
|
*/
|
||||||
public PathExtensionContentNegotiationStrategy() {
|
public PathExtensionContentNegotiationStrategy() {
|
||||||
super(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance with the given map of file extensions and media types.
|
||||||
|
*/
|
||||||
|
public PathExtensionContentNegotiationStrategy(Map<String, MediaType> mediaTypes) {
|
||||||
|
super(mediaTypes);
|
||||||
|
this.urlPathHelper.setUrlDecode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure a {@code UrlPathHelper} to use in {@link #getMediaTypeKey}
|
||||||
|
* in order to derive the lookup path for a target request URL path.
|
||||||
|
* @since 4.2.8
|
||||||
|
*/
|
||||||
|
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||||
|
this.urlPathHelper = urlPathHelper;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to use the Java Activation Framework to look up file extensions.
|
* Whether to use the Java Activation Framework to look up file extensions.
|
||||||
|
@ -113,7 +117,7 @@ public class PathExtensionContentNegotiationStrategy
|
||||||
logger.warn("An HttpServletRequest is required to determine the media type key");
|
logger.warn("An HttpServletRequest is required to determine the media type key");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String path = PATH_HELPER.getLookupPathForRequest(request);
|
String path = this.urlPathHelper.getLookupPathForRequest(request);
|
||||||
String filename = WebUtils.extractFullFilenameFromUrlPath(path);
|
String filename = WebUtils.extractFullFilenameFromUrlPath(path);
|
||||||
String extension = StringUtils.getFilenameExtension(filename);
|
String extension = StringUtils.getFilenameExtension(filename);
|
||||||
return (StringUtils.hasText(extension)) ? extension.toLowerCase(Locale.ENGLISH) : null;
|
return (StringUtils.hasText(extension)) ? extension.toLowerCase(Locale.ENGLISH) : null;
|
||||||
|
|
|
@ -523,11 +523,15 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||||
/**
|
/**
|
||||||
* Set a custom UrlPathHelper to use for the PatternsRequestCondition.
|
* Set a custom UrlPathHelper to use for the PatternsRequestCondition.
|
||||||
* <p>By default this is not set.
|
* <p>By default this is not set.
|
||||||
|
* @since 4.2.8
|
||||||
*/
|
*/
|
||||||
public void setPathHelper(UrlPathHelper pathHelper) {
|
public void setUrlPathHelper(UrlPathHelper pathHelper) {
|
||||||
this.urlPathHelper = pathHelper;
|
this.urlPathHelper = pathHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a custom UrlPathHelper to use for the PatternsRequestCondition, if any.
|
||||||
|
*/
|
||||||
public UrlPathHelper getUrlPathHelper() {
|
public UrlPathHelper getUrlPathHelper() {
|
||||||
return this.urlPathHelper;
|
return this.urlPathHelper;
|
||||||
}
|
}
|
||||||
|
@ -540,24 +544,30 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||||
this.pathMatcher = pathMatcher;
|
this.pathMatcher = pathMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a custom PathMatcher to use for the PatternsRequestCondition, if any.
|
||||||
|
*/
|
||||||
public PathMatcher getPathMatcher() {
|
public PathMatcher getPathMatcher() {
|
||||||
return this.pathMatcher;
|
return this.pathMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to apply trailing slash matching in PatternsRequestCondition.
|
* Set whether to apply trailing slash matching in PatternsRequestCondition.
|
||||||
* <p>By default this is set to 'true'.
|
* <p>By default this is set to 'true'.
|
||||||
*/
|
*/
|
||||||
public void setTrailingSlashMatch(boolean trailingSlashMatch) {
|
public void setTrailingSlashMatch(boolean trailingSlashMatch) {
|
||||||
this.trailingSlashMatch = trailingSlashMatch;
|
this.trailingSlashMatch = trailingSlashMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to apply trailing slash matching in PatternsRequestCondition.
|
||||||
|
*/
|
||||||
public boolean useTrailingSlashMatch() {
|
public boolean useTrailingSlashMatch() {
|
||||||
return this.trailingSlashMatch;
|
return this.trailingSlashMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to apply suffix pattern matching in PatternsRequestCondition.
|
* Set whether to apply suffix pattern matching in PatternsRequestCondition.
|
||||||
* <p>By default this is set to 'true'.
|
* <p>By default this is set to 'true'.
|
||||||
* @see #setRegisteredSuffixPatternMatch(boolean)
|
* @see #setRegisteredSuffixPatternMatch(boolean)
|
||||||
*/
|
*/
|
||||||
|
@ -565,14 +575,17 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||||
this.suffixPatternMatch = suffixPatternMatch;
|
this.suffixPatternMatch = suffixPatternMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to apply suffix pattern matching in PatternsRequestCondition.
|
||||||
|
*/
|
||||||
public boolean useSuffixPatternMatch() {
|
public boolean useSuffixPatternMatch() {
|
||||||
return this.suffixPatternMatch;
|
return this.suffixPatternMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether suffix pattern matching should be restricted to registered
|
* Set whether suffix pattern matching should be restricted to registered
|
||||||
* file extensions only. Setting this property also sets
|
* file extensions only. Setting this property also sets
|
||||||
* suffixPatternMatch=true and requires that a
|
* {@code suffixPatternMatch=true} and requires that a
|
||||||
* {@link #setContentNegotiationManager} is also configured in order to
|
* {@link #setContentNegotiationManager} is also configured in order to
|
||||||
* obtain the registered file extensions.
|
* obtain the registered file extensions.
|
||||||
*/
|
*/
|
||||||
|
@ -581,6 +594,10 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||||
this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
|
this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether suffix pattern matching should be restricted to registered
|
||||||
|
* file extensions only.
|
||||||
|
*/
|
||||||
public boolean useRegisteredSuffixPatternMatch() {
|
public boolean useRegisteredSuffixPatternMatch() {
|
||||||
return this.registeredSuffixPatternMatch;
|
return this.registeredSuffixPatternMatch;
|
||||||
}
|
}
|
||||||
|
@ -601,10 +618,14 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||||
* Set the ContentNegotiationManager to use for the ProducesRequestCondition.
|
* Set the ContentNegotiationManager to use for the ProducesRequestCondition.
|
||||||
* <p>By default this is not set.
|
* <p>By default this is not set.
|
||||||
*/
|
*/
|
||||||
public void setContentNegotiationManager(ContentNegotiationManager manager) {
|
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
|
||||||
this.contentNegotiationManager = manager;
|
this.contentNegotiationManager = contentNegotiationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the ContentNegotiationManager to use for the ProducesRequestCondition,
|
||||||
|
* if any.
|
||||||
|
*/
|
||||||
public ContentNegotiationManager getContentNegotiationManager() {
|
public ContentNegotiationManager getContentNegotiationManager() {
|
||||||
return this.contentNegotiationManager;
|
return this.contentNegotiationManager;
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() {
|
public void afterPropertiesSet() {
|
||||||
this.config = new RequestMappingInfo.BuilderConfiguration();
|
this.config = new RequestMappingInfo.BuilderConfiguration();
|
||||||
this.config.setPathHelper(getUrlPathHelper());
|
this.config.setUrlPathHelper(getUrlPathHelper());
|
||||||
this.config.setPathMatcher(getPathMatcher());
|
this.config.setPathMatcher(getPathMatcher());
|
||||||
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
|
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
|
||||||
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
|
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import org.springframework.web.util.UrlPathHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A filter that wraps the {@link HttpServletResponse} and overrides its
|
* A filter that wraps the {@link HttpServletResponse} and overrides its
|
||||||
|
@ -96,13 +97,14 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private void initLookupPath(ResourceUrlProvider urlProvider) {
|
private void initLookupPath(ResourceUrlProvider urlProvider) {
|
||||||
if (this.indexLookupPath == null) {
|
if (this.indexLookupPath == null) {
|
||||||
String requestUri = urlProvider.getPathHelper().getRequestUri(this.request);
|
UrlPathHelper pathHelper = urlProvider.getUrlPathHelper();
|
||||||
String lookupPath = urlProvider.getPathHelper().getLookupPathForRequest(this.request);
|
String requestUri = pathHelper.getRequestUri(this.request);
|
||||||
|
String lookupPath = pathHelper.getLookupPathForRequest(this.request);
|
||||||
this.indexLookupPath = requestUri.lastIndexOf(lookupPath);
|
this.indexLookupPath = requestUri.lastIndexOf(lookupPath);
|
||||||
this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath);
|
this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath);
|
||||||
|
|
||||||
if ("/".equals(lookupPath) && !"/".equals(requestUri)) {
|
if ("/".equals(lookupPath) && !"/".equals(requestUri)) {
|
||||||
String contextPath = urlProvider.getPathHelper().getContextPath(this.request);
|
String contextPath = pathHelper.getContextPath(this.request);
|
||||||
if (requestUri.equals(contextPath)) {
|
if (requestUri.equals(contextPath)) {
|
||||||
this.indexLookupPath = requestUri.length();
|
this.indexLookupPath = requestUri.length();
|
||||||
this.prefixLookupPath = requestUri;
|
this.prefixLookupPath = requestUri;
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
private UrlPathHelper pathHelper = new UrlPathHelper();
|
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||||
|
|
||||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
|
@ -65,15 +65,16 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||||
* {@link #getForRequestUrl(javax.servlet.http.HttpServletRequest, String)}
|
* {@link #getForRequestUrl(javax.servlet.http.HttpServletRequest, String)}
|
||||||
* in order to derive the lookup path for a target request URL path.
|
* in order to derive the lookup path for a target request URL path.
|
||||||
*/
|
*/
|
||||||
public void setUrlPathHelper(UrlPathHelper pathHelper) {
|
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||||
this.pathHelper = pathHelper;
|
this.urlPathHelper = urlPathHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the configured {@code UrlPathHelper}.
|
* Return the configured {@code UrlPathHelper}.
|
||||||
|
* @since 4.2.8
|
||||||
*/
|
*/
|
||||||
public UrlPathHelper getPathHelper() {
|
public UrlPathHelper getUrlPathHelper() {
|
||||||
return this.pathHelper;
|
return this.urlPathHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -135,6 +136,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void detectResourceHandlers(ApplicationContext appContext) {
|
protected void detectResourceHandlers(ApplicationContext appContext) {
|
||||||
logger.debug("Looking for resource handler mappings");
|
logger.debug("Looking for resource handler mappings");
|
||||||
|
|
||||||
|
@ -158,7 +160,6 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A variation on {@link #getForLookupPath(String)} that accepts a full request
|
* A variation on {@link #getForLookupPath(String)} that accepts a full request
|
||||||
* URL path (i.e. including context and servlet path) and returns the full request
|
* URL path (i.e. including context and servlet path) and returns the full request
|
||||||
|
@ -181,8 +182,9 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getLookupPathIndex(HttpServletRequest request) {
|
private int getLookupPathIndex(HttpServletRequest request) {
|
||||||
String requestUri = getPathHelper().getRequestUri(request);
|
UrlPathHelper pathHelper = getUrlPathHelper();
|
||||||
String lookupPath = getPathHelper().getLookupPathForRequest(request);
|
String requestUri = pathHelper.getRequestUri(request);
|
||||||
|
String lookupPath = pathHelper.getLookupPathForRequest(request);
|
||||||
return requestUri.indexOf(lookupPath);
|
return requestUri.indexOf(lookupPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue