|
|
@ -31,8 +31,8 @@ import org.apache.commons.logging.Log; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.InitializingBean; |
|
|
|
import org.springframework.beans.factory.InitializingBean; |
|
|
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory; |
|
|
|
|
|
|
|
import org.springframework.context.ApplicationContext; |
|
|
|
import org.springframework.context.ApplicationContext; |
|
|
|
|
|
|
|
import org.springframework.context.EmbeddedValueResolverAware; |
|
|
|
import org.springframework.core.io.Resource; |
|
|
|
import org.springframework.core.io.Resource; |
|
|
|
import org.springframework.core.io.UrlResource; |
|
|
|
import org.springframework.core.io.UrlResource; |
|
|
|
import org.springframework.http.HttpHeaders; |
|
|
|
import org.springframework.http.HttpHeaders; |
|
|
@ -49,6 +49,7 @@ import org.springframework.util.CollectionUtils; |
|
|
|
import org.springframework.util.ObjectUtils; |
|
|
|
import org.springframework.util.ObjectUtils; |
|
|
|
import org.springframework.util.ResourceUtils; |
|
|
|
import org.springframework.util.ResourceUtils; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
|
|
|
|
import org.springframework.util.StringValueResolver; |
|
|
|
import org.springframework.web.HttpRequestHandler; |
|
|
|
import org.springframework.web.HttpRequestHandler; |
|
|
|
import org.springframework.web.accept.ContentNegotiationManager; |
|
|
|
import org.springframework.web.accept.ContentNegotiationManager; |
|
|
|
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; |
|
|
|
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; |
|
|
@ -93,19 +94,19 @@ import org.springframework.web.util.UrlPathHelper; |
|
|
|
* @since 3.0.4 |
|
|
|
* @since 3.0.4 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
implements HttpRequestHandler, InitializingBean, CorsConfigurationSource { |
|
|
|
implements HttpRequestHandler, EmbeddedValueResolverAware, InitializingBean, CorsConfigurationSource { |
|
|
|
|
|
|
|
|
|
|
|
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class); |
|
|
|
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class); |
|
|
|
|
|
|
|
|
|
|
|
private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset="; |
|
|
|
private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset="; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final List<String> locationValues = new ArrayList<>(4); |
|
|
|
|
|
|
|
|
|
|
|
private final List<Resource> locations = new ArrayList<>(4); |
|
|
|
private final List<Resource> locations = new ArrayList<>(4); |
|
|
|
|
|
|
|
|
|
|
|
private final Map<Resource, Charset> locationCharsets = new HashMap<>(4); |
|
|
|
private final Map<Resource, Charset> locationCharsets = new HashMap<>(4); |
|
|
|
|
|
|
|
|
|
|
|
private final List<String> locationValues = new ArrayList<>(4); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final List<ResourceResolver> resourceResolvers = new ArrayList<>(4); |
|
|
|
private final List<ResourceResolver> resourceResolvers = new ArrayList<>(4); |
|
|
|
|
|
|
|
|
|
|
|
private final List<ResourceTransformer> resourceTransformers = new ArrayList<>(4); |
|
|
|
private final List<ResourceTransformer> resourceTransformers = new ArrayList<>(4); |
|
|
@ -128,12 +129,29 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private UrlPathHelper urlPathHelper; |
|
|
|
private UrlPathHelper urlPathHelper; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private StringValueResolver embeddedValueResolver; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ResourceHttpRequestHandler() { |
|
|
|
public ResourceHttpRequestHandler() { |
|
|
|
super(HttpMethod.GET.name(), HttpMethod.HEAD.name()); |
|
|
|
super(HttpMethod.GET.name(), HttpMethod.HEAD.name()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* An alternative to {@link #setLocations(List)} that accepts a list of |
|
|
|
|
|
|
|
* String-based location values, with support for {@link UrlResource}'s |
|
|
|
|
|
|
|
* (e.g. files or HTTP URLs) with a special prefix to indicate the charset |
|
|
|
|
|
|
|
* to use when appending relative paths. For example |
|
|
|
|
|
|
|
* {@code "[charset=Windows-31J]http://example.org/path"}. |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setLocationValues(List<String> locationValues) { |
|
|
|
|
|
|
|
Assert.notNull(locationValues, "Location values list must not be null"); |
|
|
|
|
|
|
|
this.locationValues.clear(); |
|
|
|
|
|
|
|
this.locationValues.addAll(locationValues); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Set the {@code List} of {@code Resource} locations to use as sources |
|
|
|
* Set the {@code List} of {@code Resource} locations to use as sources |
|
|
|
* for serving static resources. |
|
|
|
* for serving static resources. |
|
|
@ -147,28 +165,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Return the configured {@code List} of {@code Resource} locations. |
|
|
|
* Return the configured {@code List} of {@code Resource} locations. |
|
|
|
* Note that if {@link #setLocationValues(List) locationValues} are provided, |
|
|
|
* <p>Note that if {@link #setLocationValues(List) locationValues} are provided, |
|
|
|
* instead of loaded Resource-based locations, this method will return |
|
|
|
* instead of loaded Resource-based locations, this method will return |
|
|
|
* empty until after initialization via {@link #afterPropertiesSet()}. |
|
|
|
* empty until after initialization via {@link #afterPropertiesSet()}. |
|
|
|
|
|
|
|
* @see #setLocationValues |
|
|
|
|
|
|
|
* @see #setLocations |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public List<Resource> getLocations() { |
|
|
|
public List<Resource> getLocations() { |
|
|
|
return this.locations; |
|
|
|
return this.locations; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* An alternative to {@link #setLocations(List)} that accepts a list of |
|
|
|
|
|
|
|
* String-based location values, with support for {@link UrlResource}'s |
|
|
|
|
|
|
|
* (e.g. files or HTTP URLs) with a special prefix to indicate the charset |
|
|
|
|
|
|
|
* to use when appending relative paths. For example |
|
|
|
|
|
|
|
* {@code "[charset=Windows-31J]http://example.org/path"}. |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setLocationValues(List<String> locationValues) { |
|
|
|
|
|
|
|
Assert.notNull(locationValues, "Location values list must not be null"); |
|
|
|
|
|
|
|
this.locationValues.clear(); |
|
|
|
|
|
|
|
this.locationValues.addAll(locationValues); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Configure the list of {@link ResourceResolver}s to use. |
|
|
|
* Configure the list of {@link ResourceResolver}s to use. |
|
|
|
* <p>By default {@link PathResourceResolver} is configured. If using this property, |
|
|
|
* <p>By default {@link PathResourceResolver} is configured. If using this property, |
|
|
@ -246,7 +252,6 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
* Configure a {@code ContentNegotiationManager} to help determine the |
|
|
|
* Configure a {@code ContentNegotiationManager} to help determine the |
|
|
|
* media types for resources being served. If the manager contains a path |
|
|
|
* media types for resources being served. If the manager contains a path |
|
|
|
* extension strategy it will be checked for registered file extension. |
|
|
|
* extension strategy it will be checked for registered file extension. |
|
|
|
* @param contentNegotiationManager the manager in use |
|
|
|
|
|
|
|
* @since 4.3 |
|
|
|
* @since 4.3 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setContentNegotiationManager(@Nullable ContentNegotiationManager contentNegotiationManager) { |
|
|
|
public void setContentNegotiationManager(@Nullable ContentNegotiationManager contentNegotiationManager) { |
|
|
@ -298,11 +303,15 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
return this.urlPathHelper; |
|
|
|
return this.urlPathHelper; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void setEmbeddedValueResolver(StringValueResolver resolver) { |
|
|
|
|
|
|
|
this.embeddedValueResolver = resolver; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void afterPropertiesSet() throws Exception { |
|
|
|
public void afterPropertiesSet() throws Exception { |
|
|
|
|
|
|
|
resolveResourceLocations(); |
|
|
|
loadResourceLocations(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) { |
|
|
|
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) { |
|
|
|
logger.warn("Locations list is empty. No resources will be served unless a " + |
|
|
|
logger.warn("Locations list is empty. No resources will be served unless a " + |
|
|
@ -325,23 +334,23 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
this.contentNegotiationStrategy = initContentNegotiationStrategy(); |
|
|
|
this.contentNegotiationStrategy = initContentNegotiationStrategy(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void loadResourceLocations() { |
|
|
|
private void resolveResourceLocations() { |
|
|
|
if (!CollectionUtils.isEmpty(this.locations) && !CollectionUtils.isEmpty(this.locationValues)) { |
|
|
|
|
|
|
|
throw new IllegalArgumentException("Please set either Resource-based \"locations\" or " + |
|
|
|
|
|
|
|
"String-based \"locationValues\", but not both."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(this.locationValues)) { |
|
|
|
if (CollectionUtils.isEmpty(this.locationValues)) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
ApplicationContext appContext = obtainApplicationContext(); |
|
|
|
else if (!CollectionUtils.isEmpty(this.locations)) { |
|
|
|
ConfigurableBeanFactory beanFactory = null; |
|
|
|
throw new IllegalArgumentException("Please set either Resource-based \"locations\" or " + |
|
|
|
if (appContext.getAutowireCapableBeanFactory() instanceof ConfigurableBeanFactory) { |
|
|
|
"String-based \"locationValues\", but not both."); |
|
|
|
beanFactory = ((ConfigurableBeanFactory) appContext.getAutowireCapableBeanFactory()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ApplicationContext applicationContext = obtainApplicationContext(); |
|
|
|
for (String location : this.locationValues) { |
|
|
|
for (String location : this.locationValues) { |
|
|
|
if (beanFactory != null) { |
|
|
|
if (this.embeddedValueResolver != null) { |
|
|
|
location = beanFactory.resolveEmbeddedValue(location); |
|
|
|
String resolvedLocation = this.embeddedValueResolver.resolveStringValue(location); |
|
|
|
Assert.notNull(location, "Null location"); |
|
|
|
if (resolvedLocation == null) { |
|
|
|
|
|
|
|
throw new IllegalArgumentException("Location resolved to null: " + location); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
location = resolvedLocation; |
|
|
|
} |
|
|
|
} |
|
|
|
Charset charset = null; |
|
|
|
Charset charset = null; |
|
|
|
location = location.trim(); |
|
|
|
location = location.trim(); |
|
|
@ -354,7 +363,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
charset = Charset.forName(value); |
|
|
|
charset = Charset.forName(value); |
|
|
|
location = location.substring(endIndex + 1); |
|
|
|
location = location.substring(endIndex + 1); |
|
|
|
} |
|
|
|
} |
|
|
|
Resource resource = appContext.getResource(location); |
|
|
|
Resource resource = applicationContext.getResource(location); |
|
|
|
this.locations.add(resource); |
|
|
|
this.locations.add(resource); |
|
|
|
if (charset != null) { |
|
|
|
if (charset != null) { |
|
|
|
if (!(resource instanceof UrlResource)) { |
|
|
|
if (!(resource instanceof UrlResource)) { |
|
|
|