|
|
|
@ -18,8 +18,14 @@ package org.springframework.web.servlet.resource; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
import java.net.URLDecoder; |
|
|
|
import java.net.URLDecoder; |
|
|
|
|
|
|
|
import java.nio.charset.Charset; |
|
|
|
|
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Arrays; |
|
|
|
|
|
|
|
import java.util.Collections; |
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.StringTokenizer; |
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.core.io.ClassPathResource; |
|
|
|
import org.springframework.core.io.ClassPathResource; |
|
|
|
@ -28,6 +34,8 @@ import org.springframework.core.io.UrlResource; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.web.context.support.ServletContextResource; |
|
|
|
import org.springframework.web.context.support.ServletContextResource; |
|
|
|
|
|
|
|
import org.springframework.web.util.UriUtils; |
|
|
|
|
|
|
|
import org.springframework.web.util.UrlPathHelper; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 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 |
|
|
|
@ -46,6 +54,11 @@ public class PathResourceResolver extends AbstractResourceResolver { |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private Resource[] allowedLocations; |
|
|
|
private Resource[] allowedLocations; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final Map<Resource, Charset> locationCharsets = new HashMap<>(4); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private UrlPathHelper urlPathHelper; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* By default when a Resource is found, the path of the resolved resource is |
|
|
|
* By default when a Resource is found, the path of the resolved resource is |
|
|
|
@ -73,29 +86,75 @@ public class PathResourceResolver extends AbstractResourceResolver { |
|
|
|
return this.allowedLocations; |
|
|
|
return this.allowedLocations; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Configure charsets associated with locations. If a static resource is found |
|
|
|
|
|
|
|
* under a {@link org.springframework.core.io.UrlResource URL resource} |
|
|
|
|
|
|
|
* location the charset is used to encode the relative path |
|
|
|
|
|
|
|
* <p><strong>Note:</strong> the charset is used only if the |
|
|
|
|
|
|
|
* {@link #setUrlPathHelper urlPathHelper} property is also configured and |
|
|
|
|
|
|
|
* its {@code urlDecode} property is set to true. |
|
|
|
|
|
|
|
* @param locationCharsets charsets by location |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setLocationCharsets(Map<Resource, Charset> locationCharsets) { |
|
|
|
|
|
|
|
this.locationCharsets.clear(); |
|
|
|
|
|
|
|
this.locationCharsets.putAll(locationCharsets); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Return charsets associated with static resource locations. |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public Map<Resource, Charset> getLocationCharsets() { |
|
|
|
|
|
|
|
return Collections.unmodifiableMap(locationCharsets); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Provide a reference to the {@link UrlPathHelper} used to map requests to |
|
|
|
|
|
|
|
* static resources. This helps to derive information about the lookup path |
|
|
|
|
|
|
|
* such as whether it is decoded or not. |
|
|
|
|
|
|
|
* @param urlPathHelper a reference to the path helper |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setUrlPathHelper(@Nullable UrlPathHelper urlPathHelper) { |
|
|
|
|
|
|
|
this.urlPathHelper = urlPathHelper; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* The configured {@link UrlPathHelper}. |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
public UrlPathHelper getUrlPathHelper() { |
|
|
|
|
|
|
|
return this.urlPathHelper; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected Resource resolveResourceInternal(@Nullable HttpServletRequest request, String requestPath, |
|
|
|
protected Resource resolveResourceInternal(@Nullable HttpServletRequest request, String requestPath, |
|
|
|
List<? extends Resource> locations, ResourceResolverChain chain) { |
|
|
|
List<? extends Resource> locations, ResourceResolverChain chain) { |
|
|
|
|
|
|
|
|
|
|
|
return getResource(requestPath, locations); |
|
|
|
return getResource(requestPath, request, locations); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected String resolveUrlPathInternal(String resourcePath, List<? extends Resource> locations, |
|
|
|
protected String resolveUrlPathInternal(String resourcePath, List<? extends Resource> locations, |
|
|
|
ResourceResolverChain chain) { |
|
|
|
ResourceResolverChain chain) { |
|
|
|
|
|
|
|
|
|
|
|
return (StringUtils.hasText(resourcePath) && getResource(resourcePath, locations) != null ? resourcePath : null); |
|
|
|
return (StringUtils.hasText(resourcePath) && |
|
|
|
|
|
|
|
getResource(resourcePath, null, locations) != null ? resourcePath : null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private Resource getResource(String resourcePath, List<? extends Resource> locations) { |
|
|
|
private Resource getResource(String resourcePath, @Nullable HttpServletRequest request, |
|
|
|
|
|
|
|
List<? extends Resource> locations) { |
|
|
|
|
|
|
|
|
|
|
|
for (Resource location : locations) { |
|
|
|
for (Resource location : locations) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Checking location: " + location); |
|
|
|
logger.trace("Checking location: " + location); |
|
|
|
} |
|
|
|
} |
|
|
|
Resource resource = getResource(resourcePath, location); |
|
|
|
String pathToUse = encodeIfNecessary(resourcePath, request, location); |
|
|
|
|
|
|
|
Resource resource = getResource(pathToUse, location); |
|
|
|
if (resource != null) { |
|
|
|
if (resource != null) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Found match: " + resource); |
|
|
|
logger.trace("Found match: " + resource); |
|
|
|
@ -210,4 +269,29 @@ public class PathResourceResolver extends AbstractResourceResolver { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String encodeIfNecessary(String path, @Nullable HttpServletRequest request, Resource location) { |
|
|
|
|
|
|
|
if (shouldEncodeRelativePath(location) && request != null) { |
|
|
|
|
|
|
|
Charset charset = this.locationCharsets.getOrDefault(location, StandardCharsets.UTF_8); |
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
|
|
|
StringTokenizer tokenizer = new StringTokenizer(path, "/"); |
|
|
|
|
|
|
|
while (tokenizer.hasMoreTokens()) { |
|
|
|
|
|
|
|
String value = UriUtils.encode(tokenizer.nextToken(), charset); |
|
|
|
|
|
|
|
sb.append(value); |
|
|
|
|
|
|
|
sb.append("/"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!path.endsWith("/")) { |
|
|
|
|
|
|
|
sb.setLength(sb.length() - 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return sb.toString(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
return path; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean shouldEncodeRelativePath(Resource location) { |
|
|
|
|
|
|
|
return location instanceof UrlResource && |
|
|
|
|
|
|
|
this.urlPathHelper != null && this.urlPathHelper.isUrlDecode(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|