Polish static resource handling mechanism
- ResourceResolver and ResourceResolverChain now have a consistent API with regard to method names and terminology. - ResourceResolver and ResourceResolverChain now accept List<? extends Resource> instead of List<Resource> for simplified programmatic use. - Improved Javadoc across the package. - Formatted code to align with standards. - Removed all references to ResourceUrlPathTranslator. Issue: SPR-10933
This commit is contained in:
parent
cd13b4882c
commit
3d18cfeab6
|
@ -28,30 +28,32 @@ import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A default implementation of
|
* A default implementation of {@link ResourceResolverChain} for invoking a list
|
||||||
* {@link org.springframework.web.servlet.resource.ResourceResolverChain ResourceResolverChain}
|
* of {@link ResourceResolver}s.
|
||||||
* for invoking a list of {@link ResourceResolver}s.
|
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
class DefaultResourceResolverChain implements ResourceResolverChain {
|
class DefaultResourceResolverChain implements ResourceResolverChain {
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(DefaultResourceResolverChain.class);
|
private static final Log logger = LogFactory.getLog(DefaultResourceResolverChain.class);
|
||||||
|
|
||||||
private final List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
private final List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
||||||
|
|
||||||
private int index = -1;
|
private int index = -1;
|
||||||
|
|
||||||
|
|
||||||
public DefaultResourceResolverChain(List<ResourceResolver> resolvers) {
|
public DefaultResourceResolverChain(List<? extends ResourceResolver> resolvers) {
|
||||||
this.resolvers.addAll((resolvers != null) ? resolvers : new ArrayList<ResourceResolver>());
|
if (resolvers != null) {
|
||||||
|
this.resolvers.addAll(resolvers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource resolveResource(HttpServletRequest request, String requestPath, List<Resource> locations) {
|
public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations) {
|
||||||
ResourceResolver resolver = getNextResolver();
|
ResourceResolver resolver = getNextResolver();
|
||||||
if (resolver == null) {
|
if (resolver == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -68,14 +70,14 @@ class DefaultResourceResolverChain implements ResourceResolverChain {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String resolveUrlPath(String resourcePath, List<Resource> locations) {
|
public String resolvePublicUrlPath(String resourcePath, List<? extends Resource> locations) {
|
||||||
ResourceResolver resolver = getNextResolver();
|
ResourceResolver resolver = getNextResolver();
|
||||||
if (resolver == null) {
|
if (resolver == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
logBefore(resolver);
|
logBefore(resolver);
|
||||||
String urlPath = resolver.getPublicUrlPath(resourcePath, locations, this);
|
String urlPath = resolver.resolvePublicUrlPath(resourcePath, locations, this);
|
||||||
logAfter(resolver, urlPath);
|
logAfter(resolver, urlPath);
|
||||||
return urlPath;
|
return urlPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.springframework.util.DigestUtils;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code ResourceResolver} that resolves request paths containing an additional
|
* A {@code ResourceResolver} that resolves request paths containing an additional
|
||||||
* MD5 hash in the file name.
|
* MD5 hash in the file name.
|
||||||
|
@ -48,18 +47,19 @@ import org.springframework.util.StringUtils;
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public class FingerprintResourceResolver implements ResourceResolver {
|
public class FingerprintResourceResolver implements ResourceResolver {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(FingerprintResourceResolver.class);
|
private static final Log logger = LogFactory.getLog(FingerprintResourceResolver.class);
|
||||||
|
|
||||||
private final Pattern pattern = Pattern.compile("-(\\S*)\\.");
|
private static final Pattern pattern = Pattern.compile("-(\\S*)\\.");
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource resolveResource(HttpServletRequest request, String requestPath,
|
public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations,
|
||||||
List<Resource> locations, ResourceResolverChain chain) {
|
ResourceResolverChain chain) {
|
||||||
|
|
||||||
Resource resolved = chain.resolveResource(request, requestPath, locations);
|
Resource resolved = chain.resolveResource(request, requestPath, locations);
|
||||||
if (resolved != null) {
|
if (resolved != null) {
|
||||||
|
@ -82,13 +82,13 @@ public class FingerprintResourceResolver implements ResourceResolver {
|
||||||
return baseResource;
|
return baseResource;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.debug("Potential resource found for ["+requestPath+"], but fingerprint doesn't match.");
|
logger.debug("Potential resource found for [" + requestPath + "], but fingerprint doesn't match.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractHash(String path) {
|
private String extractHash(String path) {
|
||||||
Matcher matcher = this.pattern.matcher(path);
|
Matcher matcher = pattern.matcher(path);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
String match = matcher.group(1);
|
String match = matcher.group(1);
|
||||||
return match.contains("-") ? match.substring(match.lastIndexOf("-") + 1) : match;
|
return match.contains("-") ? match.substring(match.lastIndexOf("-") + 1) : match;
|
||||||
|
@ -104,19 +104,20 @@ public class FingerprintResourceResolver implements ResourceResolver {
|
||||||
return DigestUtils.md5DigestAsHex(content);
|
return DigestUtils.md5DigestAsHex(content);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.error("Failed to calculate hash on resource [" + resource.toString()+"]");
|
logger.error("Failed to calculate hash for resource [" + resource + "]");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPublicUrlPath(String resourceUrlPath, List<Resource> locations, ResourceResolverChain chain) {
|
public String resolvePublicUrlPath(String resourceUrlPath, List<? extends Resource> locations,
|
||||||
String baseUrl = chain.resolveUrlPath(resourceUrlPath, locations);
|
ResourceResolverChain chain) {
|
||||||
|
String baseUrl = chain.resolvePublicUrlPath(resourceUrlPath, locations);
|
||||||
if (StringUtils.hasText(baseUrl)) {
|
if (StringUtils.hasText(baseUrl)) {
|
||||||
Resource original = chain.resolveResource(null, resourceUrlPath, locations);
|
Resource original = chain.resolveResource(null, resourceUrlPath, locations);
|
||||||
String hash = calculateHash(original);
|
String hash = calculateHash(original);
|
||||||
return StringUtils.stripFilenameExtension(baseUrl)
|
return StringUtils.stripFilenameExtension(baseUrl) + "-" + hash + "."
|
||||||
+ "-" + hash + "." + StringUtils.getFilenameExtension(baseUrl);
|
+ StringUtils.getFilenameExtension(baseUrl);
|
||||||
}
|
}
|
||||||
return baseUrl;
|
return baseUrl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.springframework.core.io.Resource;
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public class GzipResourceResolver implements ResourceResolver {
|
public class GzipResourceResolver implements ResourceResolver {
|
||||||
|
@ -48,8 +49,8 @@ public class GzipResourceResolver implements ResourceResolver {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource resolveResource(HttpServletRequest request, String requestPath,
|
public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations,
|
||||||
List<Resource> locations, ResourceResolverChain chain) {
|
ResourceResolverChain chain) {
|
||||||
|
|
||||||
Resource resource = chain.resolveResource(request, requestPath, locations);
|
Resource resource = chain.resolveResource(request, requestPath, locations);
|
||||||
if ((resource == null) || !isGzipAccepted(request)) {
|
if ((resource == null) || !isGzipAccepted(request)) {
|
||||||
|
@ -75,8 +76,9 @@ public class GzipResourceResolver implements ResourceResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPublicUrlPath(String resourceUrlPath, List<Resource> locations, ResourceResolverChain chain) {
|
public String resolvePublicUrlPath(String resourceUrlPath, List<? extends Resource> locations,
|
||||||
return chain.resolveUrlPath(resourceUrlPath, locations);
|
ResourceResolverChain chain) {
|
||||||
|
return chain.resolvePublicUrlPath(resourceUrlPath, locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple {@code ResourceResolver} that tries to find a resource under the given
|
* A simple {@code ResourceResolver} that tries to find a resource under the given
|
||||||
* locations matching to the request path.
|
* locations matching to the request path.
|
||||||
|
@ -35,6 +34,7 @@ import org.springframework.core.io.Resource;
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public class PathResourceResolver implements ResourceResolver {
|
public class PathResourceResolver implements ResourceResolver {
|
||||||
|
@ -43,18 +43,18 @@ public class PathResourceResolver implements ResourceResolver {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource resolveResource(HttpServletRequest request,
|
public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations,
|
||||||
String requestPath, List<Resource> locations, ResourceResolverChain chain) {
|
ResourceResolverChain chain) {
|
||||||
|
|
||||||
return getResource(requestPath, locations);
|
return getResource(requestPath, locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPublicUrlPath(String resourceUrlPath, List<Resource> locations, ResourceResolverChain chain) {
|
public String resolvePublicUrlPath(String resourceUrlPath, List<? extends Resource> locations,
|
||||||
|
ResourceResolverChain chain) {
|
||||||
return (getResource(resourceUrlPath, locations) != null) ? resourceUrlPath : null;
|
return (getResource(resourceUrlPath, locations) != null) ? resourceUrlPath : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Resource getResource(String path, List<Resource> locations) {
|
private Resource getResource(String path, List<? extends Resource> locations) {
|
||||||
for (Resource location : locations) {
|
for (Resource location : locations) {
|
||||||
try {
|
try {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.web.servlet.resource;
|
package org.springframework.web.servlet.resource;
|
||||||
|
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
@ -35,22 +36,24 @@ import java.util.List;
|
||||||
* it causes only actually modified resources to be reloaded.
|
* it causes only actually modified resources to be reloaded.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public class PrefixResourceResolver implements ResourceResolver {
|
public class PrefixResourceResolver implements ResourceResolver {
|
||||||
|
|
||||||
private final String prefix;
|
private final String prefix;
|
||||||
|
|
||||||
|
|
||||||
public PrefixResourceResolver(String prefix) {
|
public PrefixResourceResolver(String prefix) {
|
||||||
Assert.hasText(prefix, "resource path prefix should not be null");
|
Assert.hasText(prefix, "prefix must not be null or empty");
|
||||||
this.prefix = prefix.startsWith("/") ? prefix : "/" + prefix;
|
this.prefix = prefix.startsWith("/") ? prefix : "/" + prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource resolveResource(HttpServletRequest request,
|
public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations,
|
||||||
String requestPath, List<Resource> locations, ResourceResolverChain chain) {
|
ResourceResolverChain chain) {
|
||||||
|
|
||||||
if(requestPath.startsWith(this.prefix)) {
|
if (requestPath.startsWith(this.prefix)) {
|
||||||
requestPath = requestPath.substring(this.prefix.length());
|
requestPath = requestPath.substring(this.prefix.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +61,10 @@ public class PrefixResourceResolver implements ResourceResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPublicUrlPath(String resourceUrlPath, List<Resource> locations, ResourceResolverChain chain) {
|
public String resolvePublicUrlPath(String resourceUrlPath, List<? extends Resource> locations,
|
||||||
String baseUrl = chain.resolveUrlPath(resourceUrlPath, locations);
|
ResourceResolverChain chain) {
|
||||||
|
String baseUrl = chain.resolvePublicUrlPath(resourceUrlPath, locations);
|
||||||
return this.prefix + (baseUrl.startsWith("/") ? baseUrl : "/" + baseUrl);
|
return this.prefix + (baseUrl.startsWith("/") ? baseUrl : "/" + baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A central component aware of Spring MVC handler mappings for serving static
|
* A central component for serving static resources that is aware of Spring MVC
|
||||||
* resources that provides methods to determine the public URL path clients
|
* handler mappings and provides methods to determine the public URL path that
|
||||||
* should to access static resource.
|
* a client should use to access a static resource.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
@ -45,7 +45,6 @@ public class PublicResourceUrlProvider implements ApplicationListener<ContextRef
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
|
||||||
private UrlPathHelper pathHelper = new UrlPathHelper();
|
private UrlPathHelper pathHelper = new UrlPathHelper();
|
||||||
|
|
||||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
@ -154,8 +153,8 @@ public class PublicResourceUrlProvider implements ApplicationListener<ContextRef
|
||||||
* URL path to expose for public use.
|
* URL path to expose for public use.
|
||||||
*
|
*
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param requestUrl the request URL path to translate
|
* @param requestUrl the request URL path to resolve
|
||||||
* @return the translated resource URL path or {@code null}
|
* @return the resolved public URL path or {@code null} if unresolved
|
||||||
*/
|
*/
|
||||||
public final String getForRequestUrl(HttpServletRequest request, String requestUrl) {
|
public final String getForRequestUrl(HttpServletRequest request, String requestUrl) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
@ -181,19 +180,18 @@ public class PublicResourceUrlProvider implements ApplicationListener<ContextRef
|
||||||
/**
|
/**
|
||||||
* Compare the given path against configured resource handler mappings and
|
* Compare the given path against configured resource handler mappings and
|
||||||
* if a match is found use the {@code ResourceResolver} chain of the matched
|
* if a match is found use the {@code ResourceResolver} chain of the matched
|
||||||
* {@code ResourceHttpRequestHandler} to determine the URL path to expose for
|
* {@code ResourceHttpRequestHandler} to resolve the URL path to expose for
|
||||||
* public use.
|
* public use.
|
||||||
*
|
*
|
||||||
* <p>It is expected the given path is what Spring MVC would use for request
|
* <p>It is expected the given path is what Spring MVC would use for request
|
||||||
* mapping purposes, i.e. excluding context and servlet path portions.
|
* mapping purposes, i.e. excluding context and servlet path portions.
|
||||||
*
|
*
|
||||||
* @param lookupPath the look path to check
|
* @param lookupPath the lookup path to check
|
||||||
*
|
* @return the resolved public URL path or {@code null} if unresolved
|
||||||
* @return the resolved URL path or {@code null} if unresolved
|
|
||||||
*/
|
*/
|
||||||
public final String getForLookupPath(String lookupPath) {
|
public final String getForLookupPath(String lookupPath) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Checking lookup path=" + lookupPath);
|
logger.debug("Checking lookup path: " + lookupPath);
|
||||||
}
|
}
|
||||||
for (String pattern : this.handlerMap.keySet()) {
|
for (String pattern : this.handlerMap.keySet()) {
|
||||||
if (!getPathMatcher().match(pattern, lookupPath)) {
|
if (!getPathMatcher().match(pattern, lookupPath)) {
|
||||||
|
@ -207,7 +205,7 @@ public class PublicResourceUrlProvider implements ApplicationListener<ContextRef
|
||||||
}
|
}
|
||||||
ResourceHttpRequestHandler handler = this.handlerMap.get(pattern);
|
ResourceHttpRequestHandler handler = this.handlerMap.get(pattern);
|
||||||
ResourceResolverChain chain = handler.createResourceResolverChain();
|
ResourceResolverChain chain = handler.createResourceResolverChain();
|
||||||
String resolved = chain.resolveUrlPath(pathWithinMapping, handler.getLocations());
|
String resolved = chain.resolvePublicUrlPath(pathWithinMapping, handler.getLocations());
|
||||||
if (resolved == null) {
|
if (resolved == null) {
|
||||||
throw new IllegalStateException("Failed to get public resource URL path for " + pathWithinMapping);
|
throw new IllegalStateException("Failed to get public resource URL path for " + pathWithinMapping);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,8 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interceptor that exposes the
|
* An interceptor that exposes the {@link PublicResourceUrlProvider} instance it
|
||||||
* {@link PublicResourceUrlProvider ResourceUrlPathTranslator}
|
* is configured with as a request attribute.
|
||||||
* instance it is configured with as a request attribute.
|
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
@ -33,8 +32,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
public class PublicResourceUrlProviderExposingInterceptor extends HandlerInterceptorAdapter {
|
public class PublicResourceUrlProviderExposingInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of request attribute that holds
|
* Name of the request attribute that holds the {@link PublicResourceUrlProvider}.
|
||||||
* {@link PublicResourceUrlProvider ResourceUrlPathTranslator}.
|
|
||||||
*/
|
*/
|
||||||
public static final String RESOURCE_URL_PROVIDER_ATTR = PublicResourceUrlProvider.class.getName().toString();
|
public static final String RESOURCE_URL_PROVIDER_ATTR = PublicResourceUrlProvider.class.getName().toString();
|
||||||
|
|
||||||
|
|
|
@ -22,45 +22,46 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A strategy for matching a request to a server-side resource. Provides a mechanism
|
* A strategy for resolving a request to a server-side resource.
|
||||||
* for resolving an incoming request to an actual
|
*
|
||||||
* {@link org.springframework.core.io.Resource} and also for obtaining the public
|
* <p>Provides mechanisms for resolving an incoming request to an actual
|
||||||
* URL path clients should use when requesting the resource.
|
* {@link org.springframework.core.io.Resource} and for obtaining the public
|
||||||
|
* URL path that clients should use when requesting the resource.
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*
|
|
||||||
* @see org.springframework.web.servlet.resource.ResourceResolverChain
|
* @see org.springframework.web.servlet.resource.ResourceResolverChain
|
||||||
*/
|
*/
|
||||||
public interface ResourceResolver {
|
public interface ResourceResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the input request and request path to a {@link Resource} that
|
* Resolve the supplied request and request path to a {@link Resource} that
|
||||||
* exists under one of the given resource locations.
|
* exists under one of the given resource locations.
|
||||||
*
|
*
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param requestPath the portion of the request path to use
|
* @param requestPath the portion of the request path to use
|
||||||
* @param locations locations where to look for resources
|
* @param locations the locations to search in when looking up resources
|
||||||
* @param chain a chain with other resolvers to delegate to
|
* @param chain the chain of resolvers to delegate to
|
||||||
*
|
|
||||||
* @return the resolved resource or {@code null} if unresolved
|
* @return the resolved resource or {@code null} if unresolved
|
||||||
*/
|
*/
|
||||||
Resource resolveResource(HttpServletRequest request, String requestPath,
|
Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations,
|
||||||
List<Resource> locations, ResourceResolverChain chain);
|
ResourceResolverChain chain);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the externally facing public URL path for clients to use to access the
|
* Resolve the externally facing <em>public</em> URL path for clients to use
|
||||||
* resource located at the resource URL path.
|
* to access the resource that is located at the given <em>internal</em>
|
||||||
|
* resource path.
|
||||||
*
|
*
|
||||||
* @param resourceUrlPath the candidate resource URL path
|
* <p>This is useful when rendering URL links to clients.
|
||||||
* @param locations the configured locations where to look up resources
|
|
||||||
* @param chain the chain with remaining resolvers to delegate to
|
|
||||||
*
|
*
|
||||||
* @return the resolved URL path or {@code null} if unresolved
|
* @param resourcePath the internal resource path
|
||||||
|
* @param locations the locations to search in when looking up resources
|
||||||
|
* @param chain the chain of resolvers to delegate to
|
||||||
|
* @return the resolved public URL path or {@code null} if unresolved
|
||||||
*/
|
*/
|
||||||
String getPublicUrlPath(String resourceUrlPath, List<Resource> locations, ResourceResolverChain chain);
|
String resolvePublicUrlPath(String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,40 +22,40 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A contract for invoking a chain of {@link ResourceResolver}s where each resolver
|
* A contract for invoking a chain of {@link ResourceResolver}s where each resolver
|
||||||
* is given a reference to the chain allowing it to delegate when necessary.
|
* is given a reference to the chain allowing it to delegate when necessary.
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
* @see ResourceResolver
|
||||||
*/
|
*/
|
||||||
public interface ResourceResolverChain {
|
public interface ResourceResolverChain {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the URL path of an incoming request to an actual {@link Resource}
|
* Resolve the supplied request and request path to a {@link Resource} that
|
||||||
* to serve in the response.
|
* exists under one of the given resource locations.
|
||||||
*
|
*
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @param requestPath the portion of the request path to use
|
* @param requestPath the portion of the request path to use
|
||||||
* @param locations the configured locations where to look up resources
|
* @param locations the locations to search in when looking up resources
|
||||||
*
|
* @return the resolved resource or {@code null} if unresolved
|
||||||
* @return the resolved {@link Resource} or {@code null} if this resolver
|
|
||||||
* could not resolve the resource
|
|
||||||
*/
|
*/
|
||||||
Resource resolveResource(HttpServletRequest request, String requestPath, List<Resource> locations);
|
Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the given resource path to a URL path. This is useful when rendering
|
* Resolve the externally facing <em>public</em> URL path for clients to use
|
||||||
* URL links to clients to determine the actual URL to use.
|
* to access the resource that is located at the given <em>internal</em>
|
||||||
|
* resource path.
|
||||||
*
|
*
|
||||||
* @param resourcePath the resource path
|
* <p>This is useful when rendering URL links to clients.
|
||||||
* @param locations the configured locations where to look up resources
|
|
||||||
*
|
*
|
||||||
* @return the resolved URL path or {@code null} if this resolver could not
|
* @param resourcePath the internal resource path
|
||||||
* resolve the given resource path
|
* @param locations the locations to search in when looking up resources
|
||||||
|
* @return the resolved public URL path or {@code null} if unresolved
|
||||||
*/
|
*/
|
||||||
String resolveUrlPath(String resourcePath, List<Resource> locations);
|
String resolvePublicUrlPath(String resourcePath, List<? extends Resource> locations);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,16 +26,18 @@ import javax.servlet.http.HttpServletResponseWrapper;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A filter that wraps the {@link HttpServletResponse} and overrides its
|
* A filter that wraps the {@link HttpServletResponse} and overrides its
|
||||||
* {@link HttpServletResponse#encodeURL(String) encodeURL} method in order to
|
* {@link HttpServletResponse#encodeURL(String) encodeURL} method in order to
|
||||||
* translate resource request URLs.
|
* translate internal resource request URLs into public URL paths for external
|
||||||
|
* use.
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
|
public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
|
||||||
|
@ -44,16 +46,18 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
FilterChain filterChain) throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
filterChain.doFilter(request, new ResourceUrlEncodingResponseWrapper(request, response));
|
filterChain.doFilter(request, new ResourceUrlEncodingResponseWrapper(request, response));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResourceUrlEncodingResponseWrapper extends HttpServletResponseWrapper {
|
|
||||||
|
private static class ResourceUrlEncodingResponseWrapper extends HttpServletResponseWrapper {
|
||||||
|
|
||||||
private HttpServletRequest request;
|
private HttpServletRequest request;
|
||||||
|
|
||||||
|
|
||||||
private ResourceUrlEncodingResponseWrapper(HttpServletRequest request, HttpServletResponse wrapped) {
|
private ResourceUrlEncodingResponseWrapper(HttpServletRequest request, HttpServletResponse wrapped) {
|
||||||
super(wrapped);
|
super(wrapped);
|
||||||
this.request = request;
|
this.request = request;
|
||||||
|
@ -62,15 +66,15 @@ public class ResourceUrlEncodingFilter extends OncePerRequestFilter {
|
||||||
@Override
|
@Override
|
||||||
public String encodeURL(String url) {
|
public String encodeURL(String url) {
|
||||||
String name = PublicResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR;
|
String name = PublicResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR;
|
||||||
PublicResourceUrlProvider translator = (PublicResourceUrlProvider) this.request.getAttribute(name);
|
PublicResourceUrlProvider urlProvider = (PublicResourceUrlProvider) this.request.getAttribute(name);
|
||||||
if (translator != null) {
|
if (urlProvider != null) {
|
||||||
String translatedUrl = translator.getForRequestUrl(this.request, url);
|
String translatedUrl = urlProvider.getForRequestUrl(this.request, url);
|
||||||
if (translatedUrl != null) {
|
if (translatedUrl != null) {
|
||||||
return super.encodeURL(translatedUrl);
|
return super.encodeURL(translatedUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.debug("Request attribute exposing ResourceUrlPathProvider not found");
|
logger.debug("Request attribute exposing PublicResourceUrlProvider not found under name: " + name);
|
||||||
}
|
}
|
||||||
return super.encodeURL(url);
|
return super.encodeURL(url);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,58 +13,61 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.web.servlet.resource;
|
package org.springframework.web.servlet.resource;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import static org.junit.Assert.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture for {@link PrefixResourceResolver}
|
* Test fixture for {@link PrefixResourceResolver}
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Sam Brannen
|
||||||
*/
|
*/
|
||||||
public class PrefixResourceResolverTests {
|
public class PrefixResourceResolverTests {
|
||||||
|
|
||||||
private ResourceResolverChain resolver;
|
private final List<? extends Resource> locations = Arrays.asList(new ClassPathResource("test/", getClass()));
|
||||||
|
|
||||||
private List<Resource> locations;
|
|
||||||
|
|
||||||
private final String shaPrefix = "1df341f";
|
private final String shaPrefix = "1df341f";
|
||||||
|
|
||||||
|
private ResourceResolverChain chain;
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
||||||
resolvers.add(new PrefixResourceResolver(this.shaPrefix));
|
resolvers.add(new PrefixResourceResolver(this.shaPrefix));
|
||||||
resolvers.add(new PathResourceResolver());
|
resolvers.add(new PathResourceResolver());
|
||||||
this.resolver = new DefaultResourceResolverChain(resolvers);
|
this.chain = new DefaultResourceResolverChain(resolvers);
|
||||||
this.locations = new ArrayList<Resource>();
|
|
||||||
this.locations.add(new ClassPathResource("test/", getClass()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveResource() {
|
public void resolveResource() {
|
||||||
String resourceId = "foo.css";
|
String resourceId = "foo.css";
|
||||||
Resource expected = new ClassPathResource("test/foo.css", getClass());
|
Resource expected = new ClassPathResource("test/foo.css", getClass());
|
||||||
Resource actual = this.resolver.resolveResource(null, "/" + this.shaPrefix + "/" + resourceId, this.locations);
|
Resource actual = this.chain.resolveResource(null, "/" + this.shaPrefix + "/" + resourceId, this.locations);
|
||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveUrlPath() {
|
public void resolvePublicUrlPath() {
|
||||||
String resourceId = "/foo.css";
|
String resourceId = "/foo.css";
|
||||||
String url = "/" + this.shaPrefix + resourceId;
|
String url = "/" + this.shaPrefix + resourceId;
|
||||||
assertEquals(url, resolver.resolveUrlPath(resourceId, locations));
|
assertEquals(url, chain.resolvePublicUrlPath(resourceId, locations));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testFailPrefixResolverConstructor() {
|
public void constructWithEmptyPrefix() {
|
||||||
PrefixResourceResolver resolver = new PrefixResourceResolver("");
|
new PrefixResourceResolver(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -44,25 +44,25 @@ import static org.junit.Assert.*;
|
||||||
*/
|
*/
|
||||||
public class PublicResourceUrlProviderJavaConfigTests {
|
public class PublicResourceUrlProviderJavaConfigTests {
|
||||||
|
|
||||||
private MockFilterChain filterChain;
|
private final TestServlet servlet = new TestServlet();
|
||||||
|
|
||||||
private TestServlet servlet;
|
private MockFilterChain filterChain;
|
||||||
|
|
||||||
private MockHttpServletRequest request;
|
private MockHttpServletRequest request;
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@SuppressWarnings("resource")
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
|
|
||||||
this.servlet = new TestServlet();
|
|
||||||
this.filterChain = new MockFilterChain(this.servlet, new ResourceUrlEncodingFilter());
|
this.filterChain = new MockFilterChain(this.servlet, new ResourceUrlEncodingFilter());
|
||||||
|
|
||||||
AnnotationConfigWebApplicationContext cxt = new AnnotationConfigWebApplicationContext();
|
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
|
||||||
cxt.setServletContext(new MockServletContext());
|
ctx.setServletContext(new MockServletContext());
|
||||||
cxt.register(WebConfig.class);
|
ctx.register(WebConfig.class);
|
||||||
cxt.refresh();
|
ctx.refresh();
|
||||||
|
|
||||||
PublicResourceUrlProvider urlProvider = cxt.getBean(PublicResourceUrlProvider.class);
|
PublicResourceUrlProvider urlProvider = ctx.getBean(PublicResourceUrlProvider.class);
|
||||||
|
|
||||||
this.request = new MockHttpServletRequest("GET", "/");
|
this.request = new MockHttpServletRequest("GET", "/");
|
||||||
request.setAttribute(PublicResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, urlProvider);
|
request.setAttribute(PublicResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, urlProvider);
|
||||||
|
|
Loading…
Reference in New Issue