Introduce optimizeLocations flag for resource location filtering on startup
This flag is off by default since it requires jar files with directory entries. Closes gh-27624
This commit is contained in:
parent
11a0df3fd1
commit
2a268701c4
|
@ -54,6 +54,8 @@ public class ResourceHandlerRegistration {
|
||||||
|
|
||||||
private boolean useLastModified = true;
|
private boolean useLastModified = true;
|
||||||
|
|
||||||
|
private boolean optimizeLocations = false;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Map<String, MediaType> mediaTypes;
|
private Map<String, MediaType> mediaTypes;
|
||||||
|
|
||||||
|
@ -105,15 +107,33 @@ public class ResourceHandlerRegistration {
|
||||||
/**
|
/**
|
||||||
* Set whether the {@link Resource#lastModified()} information should be used to drive HTTP responses.
|
* Set whether the {@link Resource#lastModified()} information should be used to drive HTTP responses.
|
||||||
* <p>This configuration is set to {@code true} by default.
|
* <p>This configuration is set to {@code true} by default.
|
||||||
* @param useLastModified whether the "last modified" resource information should be used.
|
* @param useLastModified whether the "last modified" resource information should be used
|
||||||
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
|
* @see ResourceWebHandler#setUseLastModified
|
||||||
*/
|
*/
|
||||||
public ResourceHandlerRegistration setUseLastModified(boolean useLastModified) {
|
public ResourceHandlerRegistration setUseLastModified(boolean useLastModified) {
|
||||||
this.useLastModified = useLastModified;
|
this.useLastModified = useLastModified;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to optimize the specified locations through an existence check on startup,
|
||||||
|
* filtering non-existing directories upfront so that they do not have to be checked
|
||||||
|
* on every resource access.
|
||||||
|
* <p>The default is {@code false}, for defensiveness against zip files without directory
|
||||||
|
* entries which are unable to expose the existence of a directory upfront. Switch this flag to
|
||||||
|
* {@code true} for optimized access in case of a consistent jar layout with directory entries.
|
||||||
|
* @param optimizeLocations whether to optimize the locations through an existence check on startup
|
||||||
|
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
||||||
|
* @since 5.3.13
|
||||||
|
* @see ResourceWebHandler#setOptimizeLocations
|
||||||
|
*/
|
||||||
|
public ResourceHandlerRegistration setOptimizeLocations(boolean optimizeLocations) {
|
||||||
|
this.optimizeLocations = optimizeLocations;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a chain of resource resolvers and transformers to use. This
|
* Configure a chain of resource resolvers and transformers to use. This
|
||||||
* can be useful, for example, to apply a version strategy to resource URLs.
|
* can be useful, for example, to apply a version strategy to resource URLs.
|
||||||
|
@ -181,8 +201,8 @@ public class ResourceHandlerRegistration {
|
||||||
*/
|
*/
|
||||||
protected ResourceWebHandler getRequestHandler() {
|
protected ResourceWebHandler getRequestHandler() {
|
||||||
ResourceWebHandler handler = new ResourceWebHandler();
|
ResourceWebHandler handler = new ResourceWebHandler();
|
||||||
handler.setLocationValues(this.locationValues);
|
|
||||||
handler.setResourceLoader(this.resourceLoader);
|
handler.setResourceLoader(this.resourceLoader);
|
||||||
|
handler.setLocationValues(this.locationValues);
|
||||||
if (this.resourceChainRegistration != null) {
|
if (this.resourceChainRegistration != null) {
|
||||||
handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers());
|
handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers());
|
||||||
handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers());
|
handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers());
|
||||||
|
@ -191,6 +211,7 @@ public class ResourceHandlerRegistration {
|
||||||
handler.setCacheControl(this.cacheControl);
|
handler.setCacheControl(this.cacheControl);
|
||||||
}
|
}
|
||||||
handler.setUseLastModified(this.useLastModified);
|
handler.setUseLastModified(this.useLastModified);
|
||||||
|
handler.setOptimizeLocations(this.optimizeLocations);
|
||||||
if (this.mediaTypes != null) {
|
if (this.mediaTypes != null) {
|
||||||
handler.setMediaTypes(this.mediaTypes);
|
handler.setMediaTypes(this.mediaTypes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,8 +72,8 @@ import org.springframework.web.server.WebHandler;
|
||||||
* <p>This request handler may also be configured with a
|
* <p>This request handler may also be configured with a
|
||||||
* {@link #setResourceResolvers(List) resourcesResolver} and
|
* {@link #setResourceResolvers(List) resourcesResolver} and
|
||||||
* {@link #setResourceTransformers(List) resourceTransformer} chains to support
|
* {@link #setResourceTransformers(List) resourceTransformer} chains to support
|
||||||
* arbitrary resolution and transformation of resources being served. By default a
|
* arbitrary resolution and transformation of resources being served. By default
|
||||||
* {@link PathResourceResolver} simply finds resources based on the configured
|
* a {@link PathResourceResolver} simply finds resources based on the configured
|
||||||
* "locations". An application can configure additional resolvers and
|
* "locations". An application can configure additional resolvers and
|
||||||
* transformers such as the {@link VersionResourceResolver} which can resolve
|
* transformers such as the {@link VersionResourceResolver} which can resolve
|
||||||
* and prepare URLs for resources with a version in the URL.
|
* and prepare URLs for resources with a version in the URL.
|
||||||
|
@ -85,6 +85,7 @@ import org.springframework.web.server.WebHandler;
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class ResourceWebHandler implements WebHandler, InitializingBean {
|
public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
|
@ -94,6 +95,9 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
private static final Log logger = LogFactory.getLog(ResourceWebHandler.class);
|
private static final Log logger = LogFactory.getLog(ResourceWebHandler.class);
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ResourceLoader resourceLoader;
|
||||||
|
|
||||||
private final List<String> locationValues = new ArrayList<>(4);
|
private final List<String> locationValues = new ArrayList<>(4);
|
||||||
|
|
||||||
private final List<Resource> locationResources = new ArrayList<>(4);
|
private final List<Resource> locationResources = new ArrayList<>(4);
|
||||||
|
@ -119,11 +123,18 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
@Nullable
|
@Nullable
|
||||||
private Map<String, MediaType> mediaTypes;
|
private Map<String, MediaType> mediaTypes;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private ResourceLoader resourceLoader;
|
|
||||||
|
|
||||||
private boolean useLastModified = true;
|
private boolean useLastModified = true;
|
||||||
|
|
||||||
|
private boolean optimizeLocations = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide the ResourceLoader to load {@link #setLocationValues location values} with.
|
||||||
|
* @since 5.1
|
||||||
|
*/
|
||||||
|
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||||
|
this.resourceLoader = resourceLoader;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts a list of String-based location values to be resolved into
|
* Accepts a list of String-based location values to be resolved into
|
||||||
|
@ -161,9 +172,9 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
* <p>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()}.
|
||||||
* <p><strong>Note:</strong> As of 5.3.11 the list of locations is filtered
|
* <p><strong>Note:</strong> As of 5.3.11 the list of locations may be filtered to
|
||||||
* to exclude those that don't actually exist and therefore the list returned
|
* exclude those that don't actually exist and therefore the list returned from this
|
||||||
* from this method may be a subset of all given locations.
|
* method may be a subset of all given locations. See {@link #setOptimizeLocations}.
|
||||||
* @see #setLocationValues
|
* @see #setLocationValues
|
||||||
* @see #setLocations
|
* @see #setLocations
|
||||||
*/
|
*/
|
||||||
|
@ -212,6 +223,22 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
return this.resourceTransformers;
|
return this.resourceTransformers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the {@link ResourceHttpMessageWriter} to use.
|
||||||
|
* <p>By default a {@link ResourceHttpMessageWriter} will be configured.
|
||||||
|
*/
|
||||||
|
public void setResourceHttpMessageWriter(@Nullable ResourceHttpMessageWriter httpMessageWriter) {
|
||||||
|
this.resourceHttpMessageWriter = httpMessageWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configured resource message writer.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public ResourceHttpMessageWriter getResourceHttpMessageWriter() {
|
||||||
|
return this.resourceHttpMessageWriter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link org.springframework.http.CacheControl} instance to build
|
* Set the {@link org.springframework.http.CacheControl} instance to build
|
||||||
* the Cache-Control HTTP response header.
|
* the Cache-Control HTTP response header.
|
||||||
|
@ -230,19 +257,48 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the {@link ResourceHttpMessageWriter} to use.
|
* Set whether we should look at the {@link Resource#lastModified()}
|
||||||
* <p>By default a {@link ResourceHttpMessageWriter} will be configured.
|
* when serving resources and use this information to drive {@code "Last-Modified"}
|
||||||
|
* HTTP response headers.
|
||||||
|
* <p>This option is enabled by default and should be turned off if the metadata of
|
||||||
|
* the static files should be ignored.
|
||||||
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
public void setResourceHttpMessageWriter(@Nullable ResourceHttpMessageWriter httpMessageWriter) {
|
public void setUseLastModified(boolean useLastModified) {
|
||||||
this.resourceHttpMessageWriter = httpMessageWriter;
|
this.useLastModified = useLastModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the configured resource message writer.
|
* Return whether the {@link Resource#lastModified()} information is used
|
||||||
|
* to drive HTTP responses when serving static resources.
|
||||||
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public boolean isUseLastModified() {
|
||||||
public ResourceHttpMessageWriter getResourceHttpMessageWriter() {
|
return this.useLastModified;
|
||||||
return this.resourceHttpMessageWriter;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to optimize the specified locations through an existence
|
||||||
|
* check on startup, filtering non-existing directories upfront so that
|
||||||
|
* they do not have to be checked on every resource access.
|
||||||
|
* <p>The default is {@code false}, for defensiveness against zip files
|
||||||
|
* without directory entries which are unable to expose the existence of
|
||||||
|
* a directory upfront. Switch this flag to {@code true} for optimized
|
||||||
|
* access in case of a consistent jar layout with directory entries.
|
||||||
|
* @since 5.3.13
|
||||||
|
*/
|
||||||
|
public void setOptimizeLocations(boolean optimizeLocations) {
|
||||||
|
this.optimizeLocations = optimizeLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to optimize the specified locations through an existence
|
||||||
|
* check on startup, filtering non-existing directories upfront so that
|
||||||
|
* they do not have to be checked on every resource access.
|
||||||
|
* @since 5.3.13
|
||||||
|
*/
|
||||||
|
public boolean isOptimizeLocations() {
|
||||||
|
return this.optimizeLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,36 +325,6 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
return (this.mediaTypes != null ? this.mediaTypes : Collections.emptyMap());
|
return (this.mediaTypes != null ? this.mediaTypes : Collections.emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide the ResourceLoader to load {@link #setLocationValues(List)
|
|
||||||
* location values} with.
|
|
||||||
* @since 5.1
|
|
||||||
*/
|
|
||||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
|
||||||
this.resourceLoader = resourceLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the {@link Resource#lastModified()} information is used
|
|
||||||
* to drive HTTP responses when serving static resources.
|
|
||||||
* @since 5.3
|
|
||||||
*/
|
|
||||||
public boolean isUseLastModified() {
|
|
||||||
return this.useLastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set whether we should look at the {@link Resource#lastModified()}
|
|
||||||
* when serving resources and use this information to drive {@code "Last-Modified"}
|
|
||||||
* HTTP response headers.
|
|
||||||
* <p>This option is enabled by default and should be turned off if the metadata of
|
|
||||||
* the static files should be ignored.
|
|
||||||
* @param useLastModified whether to use the resource last-modified information.
|
|
||||||
* @since 5.3
|
|
||||||
*/
|
|
||||||
public void setUseLastModified(boolean useLastModified) {
|
|
||||||
this.useLastModified = useLastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
@ -332,7 +358,9 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = result.stream().filter(Resource::exists).collect(Collectors.toList());
|
if (isOptimizeLocations()) {
|
||||||
|
result = result.stream().filter(Resource::exists).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
this.locationsToUse.clear();
|
this.locationsToUse.clear();
|
||||||
this.locationsToUse.addAll(result);
|
this.locationsToUse.addAll(result);
|
||||||
|
|
|
@ -74,6 +74,7 @@ public class ResourceWebHandlerTests {
|
||||||
|
|
||||||
private ResourceWebHandler handler;
|
private ResourceWebHandler handler;
|
||||||
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
List<Resource> locations = new ArrayList<>(2);
|
List<Resource> locations = new ArrayList<>(2);
|
||||||
|
@ -253,7 +254,7 @@ public class ResourceWebHandlerTests {
|
||||||
assertResponseBody(exchange, "h1 { color:red; }");
|
assertResponseBody(exchange, "h1 { color:red; }");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // gh-27538
|
@Test // gh-27538, gh-27624
|
||||||
public void filterNonExistingLocations() throws Exception {
|
public void filterNonExistingLocations() throws Exception {
|
||||||
List<Resource> inputLocations = Arrays.asList(
|
List<Resource> inputLocations = Arrays.asList(
|
||||||
new ClassPathResource("test/", getClass()),
|
new ClassPathResource("test/", getClass()),
|
||||||
|
@ -262,6 +263,7 @@ public class ResourceWebHandlerTests {
|
||||||
|
|
||||||
ResourceWebHandler handler = new ResourceWebHandler();
|
ResourceWebHandler handler = new ResourceWebHandler();
|
||||||
handler.setLocations(inputLocations);
|
handler.setLocations(inputLocations);
|
||||||
|
handler.setOptimizeLocations(true);
|
||||||
handler.afterPropertiesSet();
|
handler.afterPropertiesSet();
|
||||||
|
|
||||||
List<Resource> actual = handler.getLocations();
|
List<Resource> actual = handler.getLocations();
|
||||||
|
|
|
@ -55,6 +55,8 @@ public class ResourceHandlerRegistration {
|
||||||
|
|
||||||
private boolean useLastModified = true;
|
private boolean useLastModified = true;
|
||||||
|
|
||||||
|
private boolean optimizeLocations = false;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link ResourceHandlerRegistration} instance.
|
* Create a {@link ResourceHandlerRegistration} instance.
|
||||||
|
@ -130,15 +132,33 @@ public class ResourceHandlerRegistration {
|
||||||
/**
|
/**
|
||||||
* Set whether the {@link Resource#lastModified()} information should be used to drive HTTP responses.
|
* Set whether the {@link Resource#lastModified()} information should be used to drive HTTP responses.
|
||||||
* <p>This configuration is set to {@code true} by default.
|
* <p>This configuration is set to {@code true} by default.
|
||||||
* @param useLastModified whether the "last modified" resource information should be used.
|
* @param useLastModified whether the "last modified" resource information should be used
|
||||||
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
|
* @see ResourceHttpRequestHandler#setUseLastModified
|
||||||
*/
|
*/
|
||||||
public ResourceHandlerRegistration setUseLastModified(boolean useLastModified) {
|
public ResourceHandlerRegistration setUseLastModified(boolean useLastModified) {
|
||||||
this.useLastModified = useLastModified;
|
this.useLastModified = useLastModified;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to optimize the specified locations through an existence check on startup,
|
||||||
|
* filtering non-existing directories upfront so that they do not have to be checked
|
||||||
|
* on every resource access.
|
||||||
|
* <p>The default is {@code false}, for defensiveness against zip files without directory
|
||||||
|
* entries which are unable to expose the existence of a directory upfront. Switch this flag to
|
||||||
|
* {@code true} for optimized access in case of a consistent jar layout with directory entries.
|
||||||
|
* @param optimizeLocations whether to optimize the locations through an existence check on startup
|
||||||
|
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
||||||
|
* @since 5.3.13
|
||||||
|
* @see ResourceHttpRequestHandler#setOptimizeLocations
|
||||||
|
*/
|
||||||
|
public ResourceHandlerRegistration setOptimizeLocations(boolean optimizeLocations) {
|
||||||
|
this.optimizeLocations = optimizeLocations;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure a chain of resource resolvers and transformers to use. This
|
* Configure a chain of resource resolvers and transformers to use. This
|
||||||
* can be useful, for example, to apply a version strategy to resource URLs.
|
* can be useful, for example, to apply a version strategy to resource URLs.
|
||||||
|
@ -204,6 +224,7 @@ public class ResourceHandlerRegistration {
|
||||||
handler.setCacheSeconds(this.cachePeriod);
|
handler.setCacheSeconds(this.cachePeriod);
|
||||||
}
|
}
|
||||||
handler.setUseLastModified(this.useLastModified);
|
handler.setUseLastModified(this.useLastModified);
|
||||||
|
handler.setOptimizeLocations(this.optimizeLocations);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,11 +140,13 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
@Nullable
|
@Nullable
|
||||||
private UrlPathHelper urlPathHelper;
|
private UrlPathHelper urlPathHelper;
|
||||||
|
|
||||||
|
private boolean useLastModified = true;
|
||||||
|
|
||||||
|
private boolean optimizeLocations = false;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private StringValueResolver embeddedValueResolver;
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
private boolean useLastModified = true;
|
|
||||||
|
|
||||||
|
|
||||||
public ResourceHttpRequestHandler() {
|
public ResourceHttpRequestHandler() {
|
||||||
super(HttpMethod.GET.name(), HttpMethod.HEAD.name());
|
super(HttpMethod.GET.name(), HttpMethod.HEAD.name());
|
||||||
|
@ -185,13 +187,13 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
/**
|
/**
|
||||||
* Return the configured {@code List} of {@code Resource} locations including
|
* Return the configured {@code List} of {@code Resource} locations including
|
||||||
* both String-based locations provided via
|
* both String-based locations provided via
|
||||||
* {@link #setLocationValues(List) setLocationValues} and pre-resolved {@code Resource}
|
* {@link #setLocationValues(List) setLocationValues} and pre-resolved
|
||||||
* locations provided via {@link #setLocations(List) setLocations}.
|
* {@code Resource} locations provided via {@link #setLocations(List) setLocations}.
|
||||||
* <p>Note that the returned list is fully initialized only after
|
* <p>Note that the returned list is fully initialized only after
|
||||||
* initialization via {@link #afterPropertiesSet()}.
|
* initialization via {@link #afterPropertiesSet()}.
|
||||||
* <p><strong>Note:</strong> As of 5.3.11 the list of locations is filtered
|
* <p><strong>Note:</strong> As of 5.3.11 the list of locations may be filtered to
|
||||||
* to exclude those that don't actually exist and therefore the list returned
|
* exclude those that don't actually exist and therefore the list returned from this
|
||||||
* from this method may be a subset of all given locations.
|
* method may be a subset of all given locations. See {@link #setOptimizeLocations}.
|
||||||
* @see #setLocationValues
|
* @see #setLocationValues
|
||||||
* @see #setLocations
|
* @see #setLocations
|
||||||
*/
|
*/
|
||||||
|
@ -293,7 +295,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
/**
|
/**
|
||||||
* Return the configured content negotiation manager.
|
* Return the configured content negotiation manager.
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
* @deprecated as of 5.2.4.
|
* @deprecated as of 5.2.4
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -303,7 +305,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add mappings between file extensions, extracted from the filename of a
|
* Add mappings between file extensions, extracted from the filename of a
|
||||||
* static {@link Resource}, and corresponding media type to set on the
|
* static {@link Resource}, and corresponding media type to set on the
|
||||||
* response.
|
* response.
|
||||||
* <p>Use of this method is typically not necessary since mappings are
|
* <p>Use of this method is typically not necessary since mappings are
|
||||||
* otherwise determined via
|
* otherwise determined via
|
||||||
|
@ -361,9 +363,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
return this.urlPathHelper;
|
return this.urlPathHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
* Set whether we should look at the {@link Resource#lastModified()} when
|
||||||
this.embeddedValueResolver = resolver;
|
* serving resources and use this information to drive {@code "Last-Modified"}
|
||||||
|
* HTTP response headers.
|
||||||
|
* <p>This option is enabled by default and should be turned off if the metadata
|
||||||
|
* of the static files should be ignored.
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
public void setUseLastModified(boolean useLastModified) {
|
||||||
|
this.useLastModified = useLastModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -376,18 +385,35 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether we should look at the {@link Resource#lastModified()}
|
* Set whether to optimize the specified locations through an existence
|
||||||
* when serving resources and use this information to drive {@code "Last-Modified"}
|
* check on startup, filtering non-existing directories upfront so that
|
||||||
* HTTP response headers.
|
* they do not have to be checked on every resource access.
|
||||||
* <p>This option is enabled by default and should be turned off if the metadata of
|
* <p>The default is {@code false}, for defensiveness against zip files
|
||||||
* the static files should be ignored.
|
* without directory entries which are unable to expose the existence of
|
||||||
* @param useLastModified whether to use the resource last-modified information.
|
* a directory upfront. Switch this flag to {@code true} for optimized
|
||||||
* @since 5.3
|
* access in case of a consistent jar layout with directory entries.
|
||||||
|
* @since 5.3.13
|
||||||
*/
|
*/
|
||||||
public void setUseLastModified(boolean useLastModified) {
|
public void setOptimizeLocations(boolean optimizeLocations) {
|
||||||
this.useLastModified = useLastModified;
|
this.optimizeLocations = optimizeLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to optimize the specified locations through an existence
|
||||||
|
* check on startup, filtering non-existing directories upfront so that
|
||||||
|
* they do not have to be checked on every resource access.
|
||||||
|
* @since 5.3.13
|
||||||
|
*/
|
||||||
|
public boolean isOptimizeLocations() {
|
||||||
|
return this.optimizeLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||||
|
this.embeddedValueResolver = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
resolveResourceLocations();
|
resolveResourceLocations();
|
||||||
|
@ -449,8 +475,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
if (location.equals("/") && !(resource instanceof ServletContextResource)) {
|
if (location.equals("/") && !(resource instanceof ServletContextResource)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"The String-based location \"/\" should be relative to the web application root " +
|
"The String-based location \"/\" should be relative to the web application root " +
|
||||||
"but resolved to a Resource of type: " + resource.getClass() + ". " +
|
"but resolved to a Resource of type: " + resource.getClass() + ". " +
|
||||||
"If this is intentional, please pass it as a pre-configured Resource via setLocations.");
|
"If this is intentional, please pass it as a pre-configured Resource via setLocations.");
|
||||||
}
|
}
|
||||||
result.add(resource);
|
result.add(resource);
|
||||||
if (charset != null) {
|
if (charset != null) {
|
||||||
|
@ -463,7 +489,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
result.addAll(this.locationResources);
|
result.addAll(this.locationResources);
|
||||||
result = result.stream().filter(Resource::exists).collect(Collectors.toList());
|
if (isOptimizeLocations()) {
|
||||||
|
result = result.stream().filter(Resource::exists).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
this.locationsToUse.clear();
|
this.locationsToUse.clear();
|
||||||
this.locationsToUse.addAll(result);
|
this.locationsToUse.addAll(result);
|
||||||
|
@ -508,6 +536,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a resource request.
|
* Processes a resource request.
|
||||||
* <p>Checks for the existence of the requested resource in the configured list of locations.
|
* <p>Checks for the existence of the requested resource in the configured list of locations.
|
||||||
|
|
|
@ -311,7 +311,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||||
assertThat(this.response.getContentAsString()).isEqualTo("h1 { color:red; }");
|
assertThat(this.response.getContentAsString()).isEqualTo("h1 { color:red; }");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // gh-27538
|
@Test // gh-27538, gh-27624
|
||||||
public void filterNonExistingLocations() throws Exception {
|
public void filterNonExistingLocations() throws Exception {
|
||||||
List<Resource> inputLocations = Arrays.asList(
|
List<Resource> inputLocations = Arrays.asList(
|
||||||
new ClassPathResource("test/", getClass()),
|
new ClassPathResource("test/", getClass()),
|
||||||
|
@ -321,6 +321,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||||
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
|
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
|
||||||
handler.setServletContext(new MockServletContext());
|
handler.setServletContext(new MockServletContext());
|
||||||
handler.setLocations(inputLocations);
|
handler.setLocations(inputLocations);
|
||||||
|
handler.setOptimizeLocations(true);
|
||||||
handler.afterPropertiesSet();
|
handler.afterPropertiesSet();
|
||||||
|
|
||||||
List<Resource> actual = handler.getLocations();
|
List<Resource> actual = handler.getLocations();
|
||||||
|
|
|
@ -4170,8 +4170,8 @@ the example:
|
||||||
@Override
|
@Override
|
||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
registry.addResourceHandler("/resources/**")
|
registry.addResourceHandler("/resources/**")
|
||||||
.addResourceLocations("/public", "classpath:/static/")
|
.addResourceLocations("/public", "classpath:/static/")
|
||||||
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
|
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4259,6 +4259,9 @@ re-write URLs to include the version of the jar and can also match against incom
|
||||||
without versions -- for example, from `/jquery/jquery.min.js` to
|
without versions -- for example, from `/jquery/jquery.min.js` to
|
||||||
`/jquery/1.2.0/jquery.min.js`.
|
`/jquery/1.2.0/jquery.min.js`.
|
||||||
|
|
||||||
|
TIP: The Java configuration based on `ResourceHandlerRegistry` provides further options
|
||||||
|
for fine-grained control, e.g. last-modified behavior and optimized resource resolution.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[webflux-config-path-matching]]
|
[[webflux-config-path-matching]]
|
||||||
|
|
|
@ -5738,8 +5738,8 @@ The following listing shows how to do so with Java configuration:
|
||||||
@Override
|
@Override
|
||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
registry.addResourceHandler("/resources/**")
|
registry.addResourceHandler("/resources/**")
|
||||||
.addResourceLocations("/public", "classpath:/static/")
|
.addResourceLocations("/public", "classpath:/static/")
|
||||||
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
|
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
@ -5846,6 +5846,9 @@ re-write URLs to include the version of the jar and can also match against incom
|
||||||
without versions -- for example, from `/jquery/jquery.min.js` to
|
without versions -- for example, from `/jquery/jquery.min.js` to
|
||||||
`/jquery/1.2.0/jquery.min.js`.
|
`/jquery/1.2.0/jquery.min.js`.
|
||||||
|
|
||||||
|
TIP: The Java configuration based on `ResourceHandlerRegistry` provides further options
|
||||||
|
for fine-grained control, e.g. last-modified behavior and optimized resource resolution.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-default-servlet-handler]]
|
[[mvc-default-servlet-handler]]
|
||||||
|
|
Loading…
Reference in New Issue