Configurable UrlPathHelper in PathExtensionContentNegotiationStrategy
This commit also aligns ResourceUrlProvider's and RequestMappingInfo's UrlPathHelper setter/getter signatures.
Issue: SPR-14454
(cherry picked from commit 84afc60)
			
			
This commit is contained in:
		
							parent
							
								
									be0b71ce31
								
							
						
					
					
						commit
						5c3c0f73c1
					
				| 
						 | 
				
			
			@ -52,41 +52,45 @@ import org.springframework.web.util.WebUtils;
 | 
			
		|||
 * @author Rossen Stoyanchev
 | 
			
		||||
 * @since 3.2
 | 
			
		||||
 */
 | 
			
		||||
public class PathExtensionContentNegotiationStrategy
 | 
			
		||||
		extends AbstractMappingContentNegotiationStrategy {
 | 
			
		||||
 | 
			
		||||
	private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class);
 | 
			
		||||
public class PathExtensionContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy {
 | 
			
		||||
 | 
			
		||||
	private static final boolean JAF_PRESENT = ClassUtils.isPresent("javax.activation.FileTypeMap",
 | 
			
		||||
			PathExtensionContentNegotiationStrategy.class.getClassLoader());
 | 
			
		||||
 | 
			
		||||
	private static final UrlPathHelper PATH_HELPER = new UrlPathHelper();
 | 
			
		||||
 | 
			
		||||
	static {
 | 
			
		||||
		PATH_HELPER.setUrlDecode(false);
 | 
			
		||||
	}
 | 
			
		||||
	private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class);
 | 
			
		||||
 | 
			
		||||
	private UrlPathHelper urlPathHelper = new UrlPathHelper();
 | 
			
		||||
 | 
			
		||||
	private boolean useJaf = 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
 | 
			
		||||
	 * later on if any extensions are resolved through the Java Activation framework.
 | 
			
		||||
	 */
 | 
			
		||||
	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.
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +117,7 @@ public class PathExtensionContentNegotiationStrategy
 | 
			
		|||
			logger.warn("An HttpServletRequest is required to determine the media type key");
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		String path = PATH_HELPER.getLookupPathForRequest(request);
 | 
			
		||||
		String path = this.urlPathHelper.getLookupPathForRequest(request);
 | 
			
		||||
		String filename = WebUtils.extractFullFilenameFromUrlPath(path);
 | 
			
		||||
		String extension = StringUtils.getFilenameExtension(filename);
 | 
			
		||||
		return (StringUtils.hasText(extension)) ? extension.toLowerCase(Locale.ENGLISH) : null;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -521,13 +521,25 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
 | 
			
		|||
		private ContentNegotiationManager contentNegotiationManager;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Set a custom UrlPathHelper to use for the PatternsRequestCondition.
 | 
			
		||||
		 * <p>By default this is not set.
 | 
			
		||||
		 * @deprecated as of Spring 4.2.8, in favor of {@link #setUrlPathHelper}
 | 
			
		||||
		 */
 | 
			
		||||
		@Deprecated
 | 
			
		||||
		public void setPathHelper(UrlPathHelper pathHelper) {
 | 
			
		||||
			this.urlPathHelper = pathHelper;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Set a custom UrlPathHelper to use for the PatternsRequestCondition.
 | 
			
		||||
		 * <p>By default this is not set.
 | 
			
		||||
		 * @since 4.2.8
 | 
			
		||||
		 */
 | 
			
		||||
		public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
 | 
			
		||||
			this.urlPathHelper = urlPathHelper;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return a custom UrlPathHelper to use for the PatternsRequestCondition, if any.
 | 
			
		||||
		 */
 | 
			
		||||
		public UrlPathHelper getUrlPathHelper() {
 | 
			
		||||
			return this.urlPathHelper;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -540,24 +552,30 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
 | 
			
		|||
			this.pathMatcher = pathMatcher;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return a custom PathMatcher to use for the PatternsRequestCondition, if any.
 | 
			
		||||
		 */
 | 
			
		||||
		public PathMatcher getPathMatcher() {
 | 
			
		||||
			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'.
 | 
			
		||||
		 */
 | 
			
		||||
		public void setTrailingSlashMatch(boolean trailingSlashMatch) {
 | 
			
		||||
			this.trailingSlashMatch = trailingSlashMatch;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return whether to apply trailing slash matching in PatternsRequestCondition.
 | 
			
		||||
		 */
 | 
			
		||||
		public boolean useTrailingSlashMatch() {
 | 
			
		||||
			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'.
 | 
			
		||||
		 * @see #setRegisteredSuffixPatternMatch(boolean)
 | 
			
		||||
		 */
 | 
			
		||||
| 
						 | 
				
			
			@ -565,14 +583,17 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
 | 
			
		|||
			this.suffixPatternMatch = suffixPatternMatch;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return whether to apply suffix pattern matching in PatternsRequestCondition.
 | 
			
		||||
		 */
 | 
			
		||||
		public boolean useSuffixPatternMatch() {
 | 
			
		||||
			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
 | 
			
		||||
		 * suffixPatternMatch=true and requires that a
 | 
			
		||||
		 * {@code suffixPatternMatch=true} and requires that a
 | 
			
		||||
		 * {@link #setContentNegotiationManager} is also configured in order to
 | 
			
		||||
		 * obtain the registered file extensions.
 | 
			
		||||
		 */
 | 
			
		||||
| 
						 | 
				
			
			@ -581,6 +602,10 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
 | 
			
		|||
			this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return whether suffix pattern matching should be restricted to registered
 | 
			
		||||
		 * file extensions only.
 | 
			
		||||
		 */
 | 
			
		||||
		public boolean useRegisteredSuffixPatternMatch() {
 | 
			
		||||
			return this.registeredSuffixPatternMatch;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -601,10 +626,14 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
 | 
			
		|||
		 * Set the ContentNegotiationManager to use for the ProducesRequestCondition.
 | 
			
		||||
		 * <p>By default this is not set.
 | 
			
		||||
		 */
 | 
			
		||||
		public void setContentNegotiationManager(ContentNegotiationManager manager) {
 | 
			
		||||
			this.contentNegotiationManager = manager;
 | 
			
		||||
		public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
 | 
			
		||||
			this.contentNegotiationManager = contentNegotiationManager;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return the ContentNegotiationManager to use for the ProducesRequestCondition,
 | 
			
		||||
		 * if any.
 | 
			
		||||
		 */
 | 
			
		||||
		public ContentNegotiationManager getContentNegotiationManager() {
 | 
			
		||||
			return this.contentNegotiationManager;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
 | 
			
		|||
	@Override
 | 
			
		||||
	public void afterPropertiesSet() {
 | 
			
		||||
		this.config = new RequestMappingInfo.BuilderConfiguration();
 | 
			
		||||
		this.config.setPathHelper(getUrlPathHelper());
 | 
			
		||||
		this.config.setUrlPathHelper(getUrlPathHelper());
 | 
			
		||||
		this.config.setPathMatcher(getPathMatcher());
 | 
			
		||||
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
 | 
			
		||||
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ import org.apache.commons.logging.Log;
 | 
			
		|||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
 | 
			
		||||
import org.springframework.web.filter.OncePerRequestFilter;
 | 
			
		||||
import org.springframework.web.util.UrlPathHelper;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A filter that wraps the {@link HttpServletResponse} and overrides its
 | 
			
		||||
| 
						 | 
				
			
			@ -96,13 +97,14 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
 | 
			
		|||
 | 
			
		||||
		private void initLookupPath(ResourceUrlProvider urlProvider) {
 | 
			
		||||
			if (this.indexLookupPath == null) {
 | 
			
		||||
				String requestUri = urlProvider.getPathHelper().getRequestUri(this.request);
 | 
			
		||||
				String lookupPath = urlProvider.getPathHelper().getLookupPathForRequest(this.request);
 | 
			
		||||
				UrlPathHelper pathHelper = urlProvider.getUrlPathHelper();
 | 
			
		||||
				String requestUri = pathHelper.getRequestUri(this.request);
 | 
			
		||||
				String lookupPath = pathHelper.getLookupPathForRequest(this.request);
 | 
			
		||||
				this.indexLookupPath = requestUri.lastIndexOf(lookupPath);
 | 
			
		||||
				this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath);
 | 
			
		||||
 | 
			
		||||
				if ("/".equals(lookupPath) && !"/".equals(requestUri)) {
 | 
			
		||||
					String contextPath = urlProvider.getPathHelper().getContextPath(this.request);
 | 
			
		||||
					String contextPath = pathHelper.getContextPath(this.request);
 | 
			
		||||
					if (requestUri.equals(contextPath)) {
 | 
			
		||||
						this.indexLookupPath = requestUri.length();
 | 
			
		||||
						this.prefixLookupPath = requestUri;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2015 the original author or authors.
 | 
			
		||||
 * Copyright 2002-2016 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +51,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
 | 
			
		|||
 | 
			
		||||
	protected final Log logger = LogFactory.getLog(getClass());
 | 
			
		||||
 | 
			
		||||
	private UrlPathHelper pathHelper = new UrlPathHelper();
 | 
			
		||||
	private UrlPathHelper urlPathHelper = new UrlPathHelper();
 | 
			
		||||
 | 
			
		||||
	private PathMatcher pathMatcher = new AntPathMatcher();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,15 +65,24 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
 | 
			
		|||
	 * {@link #getForRequestUrl(javax.servlet.http.HttpServletRequest, String)}
 | 
			
		||||
	 * in order to derive the lookup path for a target request URL path.
 | 
			
		||||
	 */
 | 
			
		||||
	public void setUrlPathHelper(UrlPathHelper pathHelper) {
 | 
			
		||||
		this.pathHelper = pathHelper;
 | 
			
		||||
	public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
 | 
			
		||||
		this.urlPathHelper = urlPathHelper;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return the configured {@code UrlPathHelper}.
 | 
			
		||||
	 * @since 4.2.8
 | 
			
		||||
	 */
 | 
			
		||||
	public UrlPathHelper getUrlPathHelper() {
 | 
			
		||||
		return this.urlPathHelper;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @deprecated as of Spring 4.2.8, in favor of {@link #getUrlPathHelper}
 | 
			
		||||
	 */
 | 
			
		||||
	@Deprecated
 | 
			
		||||
	public UrlPathHelper getPathHelper() {
 | 
			
		||||
		return this.pathHelper;
 | 
			
		||||
		return this.urlPathHelper;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +144,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	protected void detectResourceHandlers(ApplicationContext appContext) {
 | 
			
		||||
		logger.debug("Looking for resource handler mappings");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +168,6 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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
 | 
			
		||||
| 
						 | 
				
			
			@ -181,8 +190,9 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	private int getLookupPathIndex(HttpServletRequest request) {
 | 
			
		||||
		String requestUri = getPathHelper().getRequestUri(request);
 | 
			
		||||
		String lookupPath = getPathHelper().getLookupPathForRequest(request);
 | 
			
		||||
		UrlPathHelper pathHelper = getUrlPathHelper();
 | 
			
		||||
		String requestUri = pathHelper.getRequestUri(request);
 | 
			
		||||
		String lookupPath = pathHelper.getLookupPathForRequest(request);
 | 
			
		||||
		return requestUri.indexOf(lookupPath);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue