Merge pull request #3123 from bclozel/gh-1604
* pr/3123: Polish Improve Spring Resource Handling support
This commit is contained in:
@@ -39,6 +39,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
|
||||
import org.thymeleaf.dialect.IDialect;
|
||||
import org.thymeleaf.extras.conditionalcomments.dialect.ConditionalCommentsDialect;
|
||||
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
|
||||
@@ -56,6 +57,7 @@ import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDiale
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ThymeleafProperties.class)
|
||||
@@ -213,4 +215,16 @@ public class ThymeleafAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
protected static class ThymeleafResourceHandlingConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
|
||||
return new ResourceUrlEncodingFilter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ui.velocity.VelocityEngineFactory;
|
||||
import org.springframework.ui.velocity.VelocityEngineFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
|
||||
import org.springframework.web.servlet.view.velocity.VelocityConfig;
|
||||
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;
|
||||
import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
|
||||
@@ -50,6 +51,7 @@ import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Velocity.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Brian Clozel
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Configuration
|
||||
@@ -134,6 +136,12 @@ public class VelocityAutoConfiguration {
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
|
||||
return new ResourceUrlEncodingFilter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,15 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Properties used to configure resource handling.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Brian Clozel
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
|
||||
@@ -37,6 +40,17 @@ public class ResourceProperties {
|
||||
*/
|
||||
private boolean addMappings = true;
|
||||
|
||||
private final Chain chain = new Chain();
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void setUpDefaults() {
|
||||
if (this.chain.enabled == null && (this.chain.strategy.content.enabled
|
||||
|| this.chain.strategy.fixed.enabled)) {
|
||||
this.chain.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getCachePeriod() {
|
||||
return this.cachePeriod;
|
||||
}
|
||||
@@ -53,4 +67,154 @@ public class ResourceProperties {
|
||||
this.addMappings = addMappings;
|
||||
}
|
||||
|
||||
public Chain getChain() {
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for the Spring Resource Handling chain.
|
||||
*/
|
||||
public static class Chain {
|
||||
|
||||
/**
|
||||
* Enable the Spring Resource Handling chain. Disabled by default unless
|
||||
* at least one strategy has been enabled.
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* Enable caching in the Resource chain.
|
||||
*/
|
||||
private boolean cache = true;
|
||||
|
||||
/**
|
||||
* Enable HTML5 app cache manifest rewriting.
|
||||
*/
|
||||
private boolean html5AppCache = false;
|
||||
|
||||
private final Strategy strategy = new Strategy();
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void setCache(boolean cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public Strategy getStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
public boolean isHtml5AppCache() {
|
||||
return html5AppCache;
|
||||
}
|
||||
|
||||
public void setHtml5AppCache(boolean html5AppCache) {
|
||||
this.html5AppCache = html5AppCache;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategies for extracting and embedding a resource version in its URL path.
|
||||
*/
|
||||
public static class Strategy {
|
||||
|
||||
private final Fixed fixed = new Fixed();
|
||||
|
||||
private final Content content = new Content();
|
||||
|
||||
public Fixed getFixed() {
|
||||
return fixed;
|
||||
}
|
||||
|
||||
public Content getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Version Strategy based on content hashing.
|
||||
*/
|
||||
public static class Content {
|
||||
|
||||
/**
|
||||
* Enable the content Version Strategy.
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* Comma-separated list of patterns to apply to the Version Strategy.
|
||||
*/
|
||||
private String[] paths = new String[]{"/**"};
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String[] getPaths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
public void setPaths(String[] paths) {
|
||||
this.paths = paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Version Strategy based on a fixed version string.
|
||||
*/
|
||||
public static class Fixed {
|
||||
|
||||
/**
|
||||
* Enable the fixed Version Strategy.
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* Comma-separated list of patterns to apply to the Version Strategy.
|
||||
*/
|
||||
private String[] paths;
|
||||
|
||||
/**
|
||||
* Version string to use for the Version Strategy.
|
||||
*/
|
||||
private String version;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String[] getPaths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
public void setPaths(String[] paths) {
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.datetime.DateFormatter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.DefaultMessageCodesResolver;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
@@ -68,6 +69,8 @@ import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceChainRegistration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
||||
@@ -76,7 +79,9 @@ import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
import org.springframework.web.servlet.resource.VersionResourceResolver;
|
||||
import org.springframework.web.servlet.view.BeanNameViewResolver;
|
||||
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
@@ -255,14 +260,41 @@ public class WebMvcAutoConfiguration {
|
||||
}
|
||||
Integer cachePeriod = this.resourceProperties.getCachePeriod();
|
||||
if (!registry.hasMappingForPattern("/webjars/**")) {
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
ResourceHandlerRegistration registration = registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/")
|
||||
.setCachePeriod(cachePeriod);
|
||||
registerResourceChain(registration);
|
||||
}
|
||||
if (!registry.hasMappingForPattern("/**")) {
|
||||
registry.addResourceHandler("/**")
|
||||
ResourceHandlerRegistration registration = registry.addResourceHandler("/**")
|
||||
.addResourceLocations(RESOURCE_LOCATIONS)
|
||||
.setCachePeriod(cachePeriod);
|
||||
registerResourceChain(registration);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerResourceChain(ResourceHandlerRegistration registration) {
|
||||
ResourceProperties.Chain chainProperties = this.resourceProperties.getChain();
|
||||
if (ObjectUtils.nullSafeEquals(chainProperties.getEnabled(), Boolean.TRUE)) {
|
||||
ResourceChainRegistration chain = registration.resourceChain(chainProperties.isCache());
|
||||
boolean hasFixedVersionConfigured = chainProperties.getStrategy().getFixed().isEnabled();
|
||||
boolean hasContentVersionConfigured = chainProperties.getStrategy().getContent().isEnabled();
|
||||
if (hasFixedVersionConfigured || hasContentVersionConfigured) {
|
||||
VersionResourceResolver versionResourceResolver = new VersionResourceResolver();
|
||||
if (hasFixedVersionConfigured) {
|
||||
versionResourceResolver.addFixedVersionStrategy(
|
||||
chainProperties.getStrategy().getFixed().getVersion(),
|
||||
chainProperties.getStrategy().getFixed().getPaths());
|
||||
}
|
||||
if (hasContentVersionConfigured) {
|
||||
versionResourceResolver.
|
||||
addContentVersionStrategy(chainProperties.getStrategy().getContent().getPaths());
|
||||
}
|
||||
chain.addResolver(versionResourceResolver);
|
||||
}
|
||||
if (chainProperties.isHtml5AppCache()) {
|
||||
chain.addTransformer(new AppCacheManifestTransformer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +31,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
|
||||
import org.springframework.web.servlet.support.RequestContext;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.context.Context;
|
||||
@@ -42,6 +43,7 @@ import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -180,4 +182,12 @@ public class ThymeleafAutoConfigurationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerResourceHandlingFilter() throws Exception {
|
||||
this.context.register(ThymeleafAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(ResourceUrlEncodingFilter.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
|
||||
import org.springframework.web.servlet.support.RequestContext;
|
||||
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;
|
||||
import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
|
||||
@@ -45,6 +46,7 @@ import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
@@ -183,6 +185,12 @@ public class VelocityAutoConfigurationTests {
|
||||
assertThat(resolver, instanceOf(EmbeddedVelocityViewResolver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerResourceHandlingFilter() throws Exception {
|
||||
registerAndRefreshContext();
|
||||
assertNotNull(this.context.getBean(ResourceUrlEncodingFilter.class));
|
||||
}
|
||||
|
||||
private void registerAndRefreshContext(String... env) {
|
||||
EnvironmentTestUtils.addEnvironment(this.context, env);
|
||||
this.context.register(VelocityAutoConfiguration.class);
|
||||
|
||||
@@ -60,10 +60,21 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
|
||||
import org.springframework.web.servlet.resource.CachingResourceResolver;
|
||||
import org.springframework.web.servlet.resource.CachingResourceTransformer;
|
||||
import org.springframework.web.servlet.resource.ContentVersionStrategy;
|
||||
import org.springframework.web.servlet.resource.CssLinkResourceTransformer;
|
||||
import org.springframework.web.servlet.resource.FixedVersionStrategy;
|
||||
import org.springframework.web.servlet.resource.PathResourceResolver;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
import org.springframework.web.servlet.resource.ResourceResolver;
|
||||
import org.springframework.web.servlet.resource.ResourceTransformer;
|
||||
import org.springframework.web.servlet.resource.VersionResourceResolver;
|
||||
import org.springframework.web.servlet.view.AbstractView;
|
||||
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
@@ -82,6 +93,7 @@ import static org.junit.Assert.assertThat;
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class WebMvcAutoConfigurationTests {
|
||||
|
||||
@@ -124,6 +136,10 @@ public class WebMvcAutoConfigurationTests {
|
||||
assertThat(mappingLocations.get("/webjars/**").size(), equalTo(1));
|
||||
assertThat(mappingLocations.get("/webjars/**").get(0),
|
||||
equalTo((Resource) new ClassPathResource("/META-INF/resources/webjars/")));
|
||||
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(1));
|
||||
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(0));
|
||||
assertThat(getResourceResolvers("/**").size(), equalTo(1));
|
||||
assertThat(getResourceTransformers("/**").size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -151,6 +167,84 @@ public class WebMvcAutoConfigurationTests {
|
||||
assertThat(mappingLocations.size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceHandlerChainEnabled() throws Exception {
|
||||
load("spring.resources.chain.enabled:true");
|
||||
|
||||
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(2));
|
||||
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(1));
|
||||
assertThat(getResourceResolvers("/**").size(), equalTo(2));
|
||||
assertThat(getResourceTransformers("/**").size(), equalTo(1));
|
||||
|
||||
assertThat(getResourceResolvers("/**"), contains(instanceOf(CachingResourceResolver.class),
|
||||
instanceOf(PathResourceResolver.class)));
|
||||
assertThat(getResourceTransformers("/**"), contains(instanceOf(CachingResourceTransformer.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceHandlerFixedStrategyEnabled() throws Exception {
|
||||
load("spring.resources.chain.strategy.fixed.enabled:true",
|
||||
"spring.resources.chain.strategy.fixed.version:test",
|
||||
"spring.resources.chain.strategy.fixed.paths:/**/*.js");
|
||||
|
||||
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(3));
|
||||
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2));
|
||||
assertThat(getResourceResolvers("/**").size(), equalTo(3));
|
||||
assertThat(getResourceTransformers("/**").size(), equalTo(2));
|
||||
|
||||
assertThat(getResourceResolvers("/**"), contains(instanceOf(CachingResourceResolver.class),
|
||||
instanceOf(VersionResourceResolver.class),
|
||||
instanceOf(PathResourceResolver.class)));
|
||||
assertThat(getResourceTransformers("/**"), contains(instanceOf(CachingResourceTransformer.class),
|
||||
instanceOf(CssLinkResourceTransformer.class)));
|
||||
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers("/**").get(1);
|
||||
assertThat(resolver.getStrategyMap().get("/**/*.js"), instanceOf(FixedVersionStrategy.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceHandlerContentStrategyEnabled() throws Exception {
|
||||
load("spring.resources.chain.strategy.content.enabled:true",
|
||||
"spring.resources.chain.strategy.content.paths:/**,/*.png");
|
||||
|
||||
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(3));
|
||||
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2));
|
||||
assertThat(getResourceResolvers("/**").size(), equalTo(3));
|
||||
assertThat(getResourceTransformers("/**").size(), equalTo(2));
|
||||
|
||||
assertThat(getResourceResolvers("/**"), contains(instanceOf(CachingResourceResolver.class),
|
||||
instanceOf(VersionResourceResolver.class),
|
||||
instanceOf(PathResourceResolver.class)));
|
||||
assertThat(getResourceTransformers("/**"), contains(instanceOf(CachingResourceTransformer.class),
|
||||
instanceOf(CssLinkResourceTransformer.class)));
|
||||
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers("/**").get(1);
|
||||
assertThat(resolver.getStrategyMap().get("/*.png"), instanceOf(ContentVersionStrategy.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceHandlerChainCustomized() throws Exception {
|
||||
load("spring.resources.chain.enabled:true", "spring.resources.chain.cache:false",
|
||||
"spring.resources.chain.strategy.content.enabled:true",
|
||||
"spring.resources.chain.strategy.content.paths:/**,/*.png",
|
||||
"spring.resources.chain.strategy.fixed.enabled:true",
|
||||
"spring.resources.chain.strategy.fixed.version:test",
|
||||
"spring.resources.chain.strategy.fixed.paths:/**/*.js",
|
||||
"spring.resources.chain.html5AppCache:true");
|
||||
|
||||
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(2));
|
||||
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2));
|
||||
assertThat(getResourceResolvers("/**").size(), equalTo(2));
|
||||
assertThat(getResourceTransformers("/**").size(), equalTo(2));
|
||||
|
||||
assertThat(getResourceResolvers("/**"), contains(
|
||||
instanceOf(VersionResourceResolver.class), instanceOf(PathResourceResolver.class)));
|
||||
assertThat(getResourceTransformers("/**"), contains(instanceOf(CssLinkResourceTransformer.class),
|
||||
instanceOf(AppCacheManifestTransformer.class)));
|
||||
|
||||
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers("/**").get(0);
|
||||
assertThat(resolver.getStrategyMap().get("/*.png"), instanceOf(ContentVersionStrategy.class));
|
||||
assertThat(resolver.getStrategyMap().get("/**/*.js"), instanceOf(FixedVersionStrategy.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLocaleResolver() throws Exception {
|
||||
load(AllResources.class);
|
||||
@@ -220,6 +314,18 @@ public class WebMvcAutoConfigurationTests {
|
||||
return getMappingLocations(mapping);
|
||||
}
|
||||
|
||||
protected List<ResourceResolver> getResourceResolvers(String mapping) {
|
||||
SimpleUrlHandlerMapping handler = (SimpleUrlHandlerMapping) this.context.getBean("resourceHandlerMapping");
|
||||
ResourceHttpRequestHandler resourceHandler = (ResourceHttpRequestHandler) handler.getHandlerMap().get(mapping);
|
||||
return resourceHandler.getResourceResolvers();
|
||||
}
|
||||
|
||||
protected List<ResourceTransformer> getResourceTransformers(String mapping) {
|
||||
SimpleUrlHandlerMapping handler = (SimpleUrlHandlerMapping) this.context.getBean("resourceHandlerMapping");
|
||||
ResourceHttpRequestHandler resourceHandler = (ResourceHttpRequestHandler) handler.getHandlerMap().get(mapping);
|
||||
return resourceHandler.getResourceTransformers();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<String, List<Resource>> getMappingLocations(HandlerMapping mapping)
|
||||
throws IllegalAccessException {
|
||||
|
||||
@@ -129,6 +129,14 @@ content into your application; rather pick only the properties that you need.
|
||||
# SPRING RESOURCES HANDLING ({sc-spring-boot-autoconfigure}/web/ResourceProperties.{sc-ext}[ResourceProperties])
|
||||
spring.resources.cache-period= # cache timeouts in headers sent to browser
|
||||
spring.resources.add-mappings=true # if default mappings should be added
|
||||
spring.resources.chain.enabled=false # enable the Spring Resource Handling chain (enabled automatically if at least a strategy is enabled)
|
||||
spring.resources.chain.cache=false # enable in-memory caching of resource resolution
|
||||
spring.resources.chain.html5AppCache=false # enable HTML5 appcache manifest rewriting
|
||||
spring.resources.chain.strategy.content.enabled=false # enable a content version strategy
|
||||
spring.resources.chain.strategy.content.paths= # comma-separated list of regular expression patterns to apply the version strategy to
|
||||
spring.resources.chain.strategy.fixed.enabled=false # enable a fixed version strategy
|
||||
spring.resources.chain.strategy.fixed.paths= # comma-separated list of regular expression patterns to apply the version strategy to
|
||||
spring.resources.chain.strategy.fixed.version= # version string to use for this version strategy
|
||||
|
||||
# MULTIPART ({sc-spring-boot-autoconfigure}/web/MultipartProperties.{sc-ext}[MultipartProperties])
|
||||
multipart.enabled=true
|
||||
|
||||
@@ -1183,6 +1183,52 @@ TIP: Do not use the `src/main/webapp` directory if your application will be pack
|
||||
jar. Although this directory is a common standard, it will *only* work with war packaging
|
||||
and it will be silently ignored by most build tools if you generate a jar.
|
||||
|
||||
Spring Boot also supports advanced resource handling features provided by Spring MVC,
|
||||
allowing use cases such as cache busting static resources or using version agnostic URLs
|
||||
for Webjars.
|
||||
|
||||
For example, the following configuration will configure a cache busting solution
|
||||
for all static resources, effectively adding a content hash in URLs, such as
|
||||
`<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>`:
|
||||
|
||||
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
spring.resources.chain.strategy.content.enabled=true
|
||||
spring.resources.chain.strategy.content.paths=/**
|
||||
----
|
||||
|
||||
NOTE: Links to resources are rewritten at runtime in template, thanks to a
|
||||
`ResourceUrlEncodingFilter`, auto-configured for Thymeleaf and Velocity. You should
|
||||
manually declare this filter when using JSPs. Other template engines aren't automatically
|
||||
supported right now, but can be with custom template macros/helpers and the use of the
|
||||
{spring-javadoc}/web/servlet/resource/ResourceUrlProvider.{dc-ext}[`ResourceUrlProvider`].
|
||||
|
||||
When loading resources dynamically with, for example, a JavaScript module loader, renaming
|
||||
files is not an option. That's why other strategies are also supported and can be combined.
|
||||
A "fixed" strategy will add a static version string in the URL, without changing the file name:
|
||||
|
||||
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
spring.resources.chain.strategy.content.enabled=true
|
||||
spring.resources.chain.strategy.content.paths=/**
|
||||
spring.resources.chain.strategy.fixed.enabled=true
|
||||
spring.resources.chain.strategy.fixed.paths=/js/lib/
|
||||
spring.resources.chain.strategy.fixed.version=v12
|
||||
----
|
||||
|
||||
With this configuration, JavaScript modules located under `"/js/lib/"` will use a fixed
|
||||
versioning strategy `"/v12/js/lib/mymodule.js"` while other resources will still use
|
||||
the content one `<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>`.
|
||||
|
||||
See {sc-spring-boot-autoconfigure}/web/ResourceProperties.{sc-ext}[`ResourceProperties`]
|
||||
for more of the supported options.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
This feature has been thoroughly described in a dedicated
|
||||
https://spring.io/blog/2014/07/24/spring-framework-4-1-handling-static-web-resources[blog post]
|
||||
and in Spring Framework's {spring-reference}/#mvc-config-static-resources[reference documentation].
|
||||
====
|
||||
|
||||
|
||||
[[boot-features-spring-mvc-template-engines]]
|
||||
|
||||
Reference in New Issue
Block a user