Move spring.resources.* properties to spring.web.resources.*
Closes gh-23917
This commit is contained in:
parent
929d8fcd3c
commit
c22e655848
|
|
@ -19,9 +19,13 @@ package org.springframework.boot.autoconfigure.web;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources.Chain;
|
||||||
|
import org.springframework.boot.context.properties.bind.BindResult;
|
||||||
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
import org.springframework.context.annotation.Condition;
|
import org.springframework.context.annotation.Condition;
|
||||||
import org.springframework.context.annotation.ConditionContext;
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
|
@ -41,10 +45,11 @@ class OnEnabledResourceChainCondition extends SpringBootCondition {
|
||||||
@Override
|
@Override
|
||||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||||
ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getEnvironment();
|
ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getEnvironment();
|
||||||
boolean fixed = getEnabledProperty(environment, "strategy.fixed.", false);
|
String prefix = determineResourcePropertiesPrefix(environment);
|
||||||
boolean content = getEnabledProperty(environment, "strategy.content.", false);
|
boolean fixed = getEnabledProperty(environment, prefix, "strategy.fixed.", false);
|
||||||
Boolean chain = getEnabledProperty(environment, "", null);
|
boolean content = getEnabledProperty(environment, prefix, "strategy.content.", false);
|
||||||
Boolean match = ResourceProperties.Chain.getEnabled(fixed, content, chain);
|
Boolean chain = getEnabledProperty(environment, prefix, "", null);
|
||||||
|
Boolean match = Chain.getEnabled(fixed, content, chain);
|
||||||
ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnEnabledResourceChain.class);
|
ConditionMessage.Builder message = ConditionMessage.forCondition(ConditionalOnEnabledResourceChain.class);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
if (ClassUtils.isPresent(WEBJAR_ASSET_LOCATOR, getClass().getClassLoader())) {
|
if (ClassUtils.isPresent(WEBJAR_ASSET_LOCATOR, getClass().getClassLoader())) {
|
||||||
|
|
@ -58,8 +63,19 @@ class OnEnabledResourceChainCondition extends SpringBootCondition {
|
||||||
return ConditionOutcome.noMatch(message.because("disabled"));
|
return ConditionOutcome.noMatch(message.because("disabled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean getEnabledProperty(ConfigurableEnvironment environment, String key, Boolean defaultValue) {
|
@SuppressWarnings("deprecation")
|
||||||
String name = "spring.resources.chain." + key + "enabled";
|
private String determineResourcePropertiesPrefix(Environment environment) {
|
||||||
|
BindResult<org.springframework.boot.autoconfigure.web.ResourceProperties> result = Binder.get(environment)
|
||||||
|
.bind("spring.resources", org.springframework.boot.autoconfigure.web.ResourceProperties.class);
|
||||||
|
if (result.isBound() && result.get().hasBeenCustomized()) {
|
||||||
|
return "spring.resources.chain.";
|
||||||
|
}
|
||||||
|
return "spring.web.resources.chain.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean getEnabledProperty(ConfigurableEnvironment environment, String prefix, String key,
|
||||||
|
Boolean defaultValue) {
|
||||||
|
String name = prefix + key + "enabled";
|
||||||
return environment.getProperty(name, Boolean.class, defaultValue);
|
return environment.getProperty(name, Boolean.class, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,10 @@
|
||||||
package org.springframework.boot.autoconfigure.web;
|
package org.springframework.boot.autoconfigure.web;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||||
import org.springframework.boot.context.properties.PropertyMapper;
|
|
||||||
import org.springframework.boot.convert.DurationUnit;
|
|
||||||
import org.springframework.http.CacheControl;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Properties used to configure resource handling.
|
* Properties used to configure resource handling.
|
||||||
|
|
@ -35,137 +31,79 @@ import org.springframework.http.CacheControl;
|
||||||
* @author Venil Noronha
|
* @author Venil Noronha
|
||||||
* @author Kristine Jetzke
|
* @author Kristine Jetzke
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
|
* @deprecated since 2.4.0 in favor of {@link WebProperties.Resources}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
|
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
|
||||||
public class ResourceProperties {
|
public class ResourceProperties extends Resources {
|
||||||
|
|
||||||
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
|
|
||||||
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
|
|
||||||
* /resources/, /static/, /public/].
|
|
||||||
*/
|
|
||||||
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to enable default resource handling.
|
|
||||||
*/
|
|
||||||
private boolean addMappings = true;
|
|
||||||
|
|
||||||
private final Chain chain = new Chain();
|
private final Chain chain = new Chain();
|
||||||
|
|
||||||
private final Cache cache = new Cache();
|
private final Cache cache = new Cache();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.static-locations")
|
||||||
public String[] getStaticLocations() {
|
public String[] getStaticLocations() {
|
||||||
return this.staticLocations;
|
return super.getStaticLocations();
|
||||||
}
|
|
||||||
|
|
||||||
public void setStaticLocations(String[] staticLocations) {
|
|
||||||
this.staticLocations = appendSlashIfNecessary(staticLocations);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] appendSlashIfNecessary(String[] staticLocations) {
|
|
||||||
String[] normalized = new String[staticLocations.length];
|
|
||||||
for (int i = 0; i < staticLocations.length; i++) {
|
|
||||||
String location = staticLocations[i];
|
|
||||||
normalized[i] = location.endsWith("/") ? location : location + "/";
|
|
||||||
}
|
|
||||||
return normalized;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.add-mappings")
|
||||||
public boolean isAddMappings() {
|
public boolean isAddMappings() {
|
||||||
return this.addMappings;
|
return super.isAddMappings();
|
||||||
}
|
|
||||||
|
|
||||||
public void setAddMappings(boolean addMappings) {
|
|
||||||
this.addMappings = addMappings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Chain getChain() {
|
public Chain getChain() {
|
||||||
return this.chain;
|
return this.chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Cache getCache() {
|
public Cache getCache() {
|
||||||
return this.cache;
|
return this.cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Configuration for the Spring Resource Handling chain.
|
public static class Chain extends Resources.Chain {
|
||||||
*/
|
|
||||||
public static class Chain {
|
|
||||||
|
|
||||||
/**
|
private final org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy strategy = new org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy();
|
||||||
* Whether to enable the Spring Resource Handling chain. By default, disabled
|
|
||||||
* unless at least one strategy has been enabled.
|
|
||||||
*/
|
|
||||||
private Boolean enabled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to enable caching in the Resource chain.
|
|
||||||
*/
|
|
||||||
private boolean cache = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to enable HTML5 application cache manifest rewriting.
|
* Whether to enable HTML5 application cache manifest rewriting.
|
||||||
*/
|
*/
|
||||||
private boolean htmlApplicationCache = false;
|
private boolean htmlApplicationCache = false;
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Whether to enable resolution of already compressed resources (gzip, brotli).
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.enabled")
|
||||||
* Checks for a resource name with the '.gz' or '.br' file extensions.
|
|
||||||
*/
|
|
||||||
private boolean compressed = false;
|
|
||||||
|
|
||||||
private final Strategy strategy = new Strategy();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the resource chain is enabled. Return {@code null} if no
|
|
||||||
* specific settings are present.
|
|
||||||
* @return whether the resource chain is enabled or {@code null} if no specified
|
|
||||||
* settings are present.
|
|
||||||
*/
|
|
||||||
public Boolean getEnabled() {
|
public Boolean getEnabled() {
|
||||||
return getEnabled(getStrategy().getFixed().isEnabled(), getStrategy().getContent().isEnabled(),
|
return super.getEnabled();
|
||||||
this.enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.cache")
|
||||||
public boolean isCache() {
|
public boolean isCache() {
|
||||||
return this.cache;
|
return super.isCache();
|
||||||
}
|
|
||||||
|
|
||||||
public void setCache(boolean cache) {
|
|
||||||
this.cache = cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Strategy getStrategy() {
|
|
||||||
return this.strategy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeprecatedConfigurationProperty(reason = "The appcache manifest feature is being removed from browsers.")
|
@DeprecatedConfigurationProperty(reason = "The appcache manifest feature is being removed from browsers.")
|
||||||
@Deprecated
|
|
||||||
public boolean isHtmlApplicationCache() {
|
public boolean isHtmlApplicationCache() {
|
||||||
return this.htmlApplicationCache;
|
return this.htmlApplicationCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHtmlApplicationCache(boolean htmlApplicationCache) {
|
public void setHtmlApplicationCache(boolean htmlApplicationCache) {
|
||||||
this.htmlApplicationCache = htmlApplicationCache;
|
this.htmlApplicationCache = htmlApplicationCache;
|
||||||
|
this.customized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.compressed")
|
||||||
public boolean isCompressed() {
|
public boolean isCompressed() {
|
||||||
return this.compressed;
|
return super.isCompressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCompressed(boolean compressed) {
|
@Override
|
||||||
this.compressed = compressed;
|
public org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy getStrategy() {
|
||||||
}
|
return this.strategy;
|
||||||
|
|
||||||
static Boolean getEnabled(boolean fixedEnabled, boolean contentEnabled, Boolean chainEnabled) {
|
|
||||||
return (fixedEnabled || contentEnabled) ? Boolean.TRUE : chainEnabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -173,17 +111,20 @@ public class ResourceProperties {
|
||||||
/**
|
/**
|
||||||
* Strategies for extracting and embedding a resource version in its URL path.
|
* Strategies for extracting and embedding a resource version in its URL path.
|
||||||
*/
|
*/
|
||||||
public static class Strategy {
|
@Deprecated
|
||||||
|
public static class Strategy extends Resources.Chain.Strategy {
|
||||||
|
|
||||||
private final Fixed fixed = new Fixed();
|
private final org.springframework.boot.autoconfigure.web.ResourceProperties.Fixed fixed = new org.springframework.boot.autoconfigure.web.ResourceProperties.Fixed();
|
||||||
|
|
||||||
private final Content content = new Content();
|
private final org.springframework.boot.autoconfigure.web.ResourceProperties.Content content = new org.springframework.boot.autoconfigure.web.ResourceProperties.Content();
|
||||||
|
|
||||||
public Fixed getFixed() {
|
@Override
|
||||||
|
public org.springframework.boot.autoconfigure.web.ResourceProperties.Fixed getFixed() {
|
||||||
return this.fixed;
|
return this.fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Content getContent() {
|
@Override
|
||||||
|
public org.springframework.boot.autoconfigure.web.ResourceProperties.Content getContent() {
|
||||||
return this.content;
|
return this.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,32 +133,19 @@ public class ResourceProperties {
|
||||||
/**
|
/**
|
||||||
* Version Strategy based on content hashing.
|
* Version Strategy based on content hashing.
|
||||||
*/
|
*/
|
||||||
public static class Content {
|
@Deprecated
|
||||||
|
public static class Content extends Resources.Chain.Strategy.Content {
|
||||||
/**
|
|
||||||
* Whether to enable the content Version Strategy.
|
|
||||||
*/
|
|
||||||
private boolean enabled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Comma-separated list of patterns to apply to the content Version Strategy.
|
|
||||||
*/
|
|
||||||
private String[] paths = new String[] { "/**" };
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.strategy.content.enabled")
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return this.enabled;
|
return super.isEnabled();
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.strategy.content.paths")
|
||||||
public String[] getPaths() {
|
public String[] getPaths() {
|
||||||
return this.paths;
|
return super.getPaths();
|
||||||
}
|
|
||||||
|
|
||||||
public void setPaths(String[] paths) {
|
|
||||||
this.paths = paths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -225,45 +153,25 @@ public class ResourceProperties {
|
||||||
/**
|
/**
|
||||||
* Version Strategy based on a fixed version string.
|
* Version Strategy based on a fixed version string.
|
||||||
*/
|
*/
|
||||||
public static class Fixed {
|
@Deprecated
|
||||||
|
public static class Fixed extends Resources.Chain.Strategy.Fixed {
|
||||||
/**
|
|
||||||
* Whether to enable the fixed Version Strategy.
|
|
||||||
*/
|
|
||||||
private boolean enabled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Comma-separated list of patterns to apply to the fixed Version Strategy.
|
|
||||||
*/
|
|
||||||
private String[] paths = new String[] { "/**" };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version string to use for the fixed Version Strategy.
|
|
||||||
*/
|
|
||||||
private String version;
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.strategy.fixed.enabled")
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return this.enabled;
|
return super.isEnabled();
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.strategy.fixed.paths")
|
||||||
public String[] getPaths() {
|
public String[] getPaths() {
|
||||||
return this.paths;
|
return super.getPaths();
|
||||||
}
|
|
||||||
|
|
||||||
public void setPaths(String[] paths) {
|
|
||||||
this.paths = paths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.chain.strategy.fixed.version")
|
||||||
public String getVersion() {
|
public String getVersion() {
|
||||||
return this.version;
|
return super.getVersion();
|
||||||
}
|
|
||||||
|
|
||||||
public void setVersion(String version) {
|
|
||||||
this.version = version;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -271,30 +179,18 @@ public class ResourceProperties {
|
||||||
/**
|
/**
|
||||||
* Cache configuration.
|
* Cache configuration.
|
||||||
*/
|
*/
|
||||||
public static class Cache {
|
@Deprecated
|
||||||
|
public static class Cache extends Resources.Cache {
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache period for the resources served by the resource handler. If a duration
|
|
||||||
* suffix is not specified, seconds will be used. Can be overridden by the
|
|
||||||
* 'spring.resources.cache.cachecontrol' properties.
|
|
||||||
*/
|
|
||||||
@DurationUnit(ChronoUnit.SECONDS)
|
|
||||||
private Duration period;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache control HTTP headers, only allows valid directive combinations. Overrides
|
|
||||||
* the 'spring.resources.cache.period' property.
|
|
||||||
*/
|
|
||||||
private final Cachecontrol cachecontrol = new Cachecontrol();
|
private final Cachecontrol cachecontrol = new Cachecontrol();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.period")
|
||||||
public Duration getPeriod() {
|
public Duration getPeriod() {
|
||||||
return this.period;
|
return super.getPeriod();
|
||||||
}
|
|
||||||
|
|
||||||
public void setPeriod(Duration period) {
|
|
||||||
this.period = period;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Cachecontrol getCachecontrol() {
|
public Cachecontrol getCachecontrol() {
|
||||||
return this.cachecontrol;
|
return this.cachecontrol;
|
||||||
}
|
}
|
||||||
|
|
@ -302,196 +198,74 @@ public class ResourceProperties {
|
||||||
/**
|
/**
|
||||||
* Cache Control HTTP header configuration.
|
* Cache Control HTTP header configuration.
|
||||||
*/
|
*/
|
||||||
public static class Cachecontrol {
|
@Deprecated
|
||||||
|
public static class Cachecontrol extends Resources.Cache.Cachecontrol {
|
||||||
/**
|
|
||||||
* Maximum time the response should be cached, in seconds if no duration
|
|
||||||
* suffix is not specified.
|
|
||||||
*/
|
|
||||||
@DurationUnit(ChronoUnit.SECONDS)
|
|
||||||
private Duration maxAge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate that the cached response can be reused only if re-validated with
|
|
||||||
* the server.
|
|
||||||
*/
|
|
||||||
private Boolean noCache;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate to not cache the response in any case.
|
|
||||||
*/
|
|
||||||
private Boolean noStore;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate that once it has become stale, a cache must not use the response
|
|
||||||
* without re-validating it with the server.
|
|
||||||
*/
|
|
||||||
private Boolean mustRevalidate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate intermediaries (caches and others) that they should not transform
|
|
||||||
* the response content.
|
|
||||||
*/
|
|
||||||
private Boolean noTransform;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate that any cache may store the response.
|
|
||||||
*/
|
|
||||||
private Boolean cachePublic;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate that the response message is intended for a single user and must
|
|
||||||
* not be stored by a shared cache.
|
|
||||||
*/
|
|
||||||
private Boolean cachePrivate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Same meaning as the "must-revalidate" directive, except that it does not
|
|
||||||
* apply to private caches.
|
|
||||||
*/
|
|
||||||
private Boolean proxyRevalidate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum time the response can be served after it becomes stale, in seconds
|
|
||||||
* if no duration suffix is not specified.
|
|
||||||
*/
|
|
||||||
@DurationUnit(ChronoUnit.SECONDS)
|
|
||||||
private Duration staleWhileRevalidate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum time the response may be used when errors are encountered, in
|
|
||||||
* seconds if no duration suffix is not specified.
|
|
||||||
*/
|
|
||||||
@DurationUnit(ChronoUnit.SECONDS)
|
|
||||||
private Duration staleIfError;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum time the response should be cached by shared caches, in seconds if
|
|
||||||
* no duration suffix is not specified.
|
|
||||||
*/
|
|
||||||
@DurationUnit(ChronoUnit.SECONDS)
|
|
||||||
private Duration sMaxAge;
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.max-age")
|
||||||
public Duration getMaxAge() {
|
public Duration getMaxAge() {
|
||||||
return this.maxAge;
|
return super.getMaxAge();
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxAge(Duration maxAge) {
|
|
||||||
this.maxAge = maxAge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.no-cache")
|
||||||
public Boolean getNoCache() {
|
public Boolean getNoCache() {
|
||||||
return this.noCache;
|
return super.getNoCache();
|
||||||
}
|
|
||||||
|
|
||||||
public void setNoCache(Boolean noCache) {
|
|
||||||
this.noCache = noCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.no-store")
|
||||||
public Boolean getNoStore() {
|
public Boolean getNoStore() {
|
||||||
return this.noStore;
|
return super.getNoStore();
|
||||||
}
|
|
||||||
|
|
||||||
public void setNoStore(Boolean noStore) {
|
|
||||||
this.noStore = noStore;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.must-revalidate")
|
||||||
public Boolean getMustRevalidate() {
|
public Boolean getMustRevalidate() {
|
||||||
return this.mustRevalidate;
|
return super.getMustRevalidate();
|
||||||
}
|
|
||||||
|
|
||||||
public void setMustRevalidate(Boolean mustRevalidate) {
|
|
||||||
this.mustRevalidate = mustRevalidate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.no-transform")
|
||||||
public Boolean getNoTransform() {
|
public Boolean getNoTransform() {
|
||||||
return this.noTransform;
|
return super.getNoTransform();
|
||||||
}
|
|
||||||
|
|
||||||
public void setNoTransform(Boolean noTransform) {
|
|
||||||
this.noTransform = noTransform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.cache-public")
|
||||||
public Boolean getCachePublic() {
|
public Boolean getCachePublic() {
|
||||||
return this.cachePublic;
|
return super.getCachePublic();
|
||||||
}
|
|
||||||
|
|
||||||
public void setCachePublic(Boolean cachePublic) {
|
|
||||||
this.cachePublic = cachePublic;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.cache-private")
|
||||||
public Boolean getCachePrivate() {
|
public Boolean getCachePrivate() {
|
||||||
return this.cachePrivate;
|
return super.getCachePrivate();
|
||||||
}
|
|
||||||
|
|
||||||
public void setCachePrivate(Boolean cachePrivate) {
|
|
||||||
this.cachePrivate = cachePrivate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.proxy-revalidate")
|
||||||
public Boolean getProxyRevalidate() {
|
public Boolean getProxyRevalidate() {
|
||||||
return this.proxyRevalidate;
|
return super.getProxyRevalidate();
|
||||||
}
|
|
||||||
|
|
||||||
public void setProxyRevalidate(Boolean proxyRevalidate) {
|
|
||||||
this.proxyRevalidate = proxyRevalidate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(
|
||||||
|
replacement = "spring.web.resources.cache.cachecontrol.stale-while-revaliate")
|
||||||
public Duration getStaleWhileRevalidate() {
|
public Duration getStaleWhileRevalidate() {
|
||||||
return this.staleWhileRevalidate;
|
return super.getStaleWhileRevalidate();
|
||||||
}
|
|
||||||
|
|
||||||
public void setStaleWhileRevalidate(Duration staleWhileRevalidate) {
|
|
||||||
this.staleWhileRevalidate = staleWhileRevalidate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.stale-if-error")
|
||||||
public Duration getStaleIfError() {
|
public Duration getStaleIfError() {
|
||||||
return this.staleIfError;
|
return super.getStaleIfError();
|
||||||
}
|
|
||||||
|
|
||||||
public void setStaleIfError(Duration staleIfError) {
|
|
||||||
this.staleIfError = staleIfError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.web.resources.cache.cachecontrol.s-max-age")
|
||||||
public Duration getSMaxAge() {
|
public Duration getSMaxAge() {
|
||||||
return this.sMaxAge;
|
return super.getSMaxAge();
|
||||||
}
|
|
||||||
|
|
||||||
public void setSMaxAge(Duration sMaxAge) {
|
|
||||||
this.sMaxAge = sMaxAge;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CacheControl toHttpCacheControl() {
|
|
||||||
PropertyMapper map = PropertyMapper.get();
|
|
||||||
CacheControl control = createCacheControl();
|
|
||||||
map.from(this::getMustRevalidate).whenTrue().toCall(control::mustRevalidate);
|
|
||||||
map.from(this::getNoTransform).whenTrue().toCall(control::noTransform);
|
|
||||||
map.from(this::getCachePublic).whenTrue().toCall(control::cachePublic);
|
|
||||||
map.from(this::getCachePrivate).whenTrue().toCall(control::cachePrivate);
|
|
||||||
map.from(this::getProxyRevalidate).whenTrue().toCall(control::proxyRevalidate);
|
|
||||||
map.from(this::getStaleWhileRevalidate).whenNonNull()
|
|
||||||
.to((duration) -> control.staleWhileRevalidate(duration.getSeconds(), TimeUnit.SECONDS));
|
|
||||||
map.from(this::getStaleIfError).whenNonNull()
|
|
||||||
.to((duration) -> control.staleIfError(duration.getSeconds(), TimeUnit.SECONDS));
|
|
||||||
map.from(this::getSMaxAge).whenNonNull()
|
|
||||||
.to((duration) -> control.sMaxAge(duration.getSeconds(), TimeUnit.SECONDS));
|
|
||||||
// check if cacheControl remained untouched
|
|
||||||
if (control.getHeaderValue() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return control;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CacheControl createCacheControl() {
|
|
||||||
if (Boolean.TRUE.equals(this.noStore)) {
|
|
||||||
return CacheControl.noStore();
|
|
||||||
}
|
|
||||||
if (Boolean.TRUE.equals(this.noCache)) {
|
|
||||||
return CacheControl.noCache();
|
|
||||||
}
|
|
||||||
if (this.maxAge != null) {
|
|
||||||
return CacheControl.maxAge(this.maxAge.getSeconds(), TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
return CacheControl.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,15 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.web;
|
package org.springframework.boot.autoconfigure.web;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.PropertyMapper;
|
||||||
|
import org.springframework.boot.convert.DurationUnit;
|
||||||
|
import org.springframework.http.CacheControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ConfigurationProperties Configuration properties} for general web concerns.
|
* {@link ConfigurationProperties Configuration properties} for general web concerns.
|
||||||
|
|
@ -40,6 +46,8 @@ public class WebProperties {
|
||||||
*/
|
*/
|
||||||
private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;
|
private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;
|
||||||
|
|
||||||
|
private final Resources resources = new Resources();
|
||||||
|
|
||||||
public Locale getLocale() {
|
public Locale getLocale() {
|
||||||
return this.locale;
|
return this.locale;
|
||||||
}
|
}
|
||||||
|
|
@ -56,6 +64,10 @@ public class WebProperties {
|
||||||
this.localeResolver = localeResolver;
|
this.localeResolver = localeResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Resources getResources() {
|
||||||
|
return this.resources;
|
||||||
|
}
|
||||||
|
|
||||||
public enum LocaleResolver {
|
public enum LocaleResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -71,4 +83,517 @@ public class WebProperties {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Resources {
|
||||||
|
|
||||||
|
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
|
||||||
|
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
|
||||||
|
* /resources/, /static/, /public/].
|
||||||
|
*/
|
||||||
|
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable default resource handling.
|
||||||
|
*/
|
||||||
|
private boolean addMappings = true;
|
||||||
|
|
||||||
|
private boolean customized = false;
|
||||||
|
|
||||||
|
private final Chain chain = new Chain();
|
||||||
|
|
||||||
|
private final Cache cache = new Cache();
|
||||||
|
|
||||||
|
public String[] getStaticLocations() {
|
||||||
|
return this.staticLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStaticLocations(String[] staticLocations) {
|
||||||
|
this.staticLocations = appendSlashIfNecessary(staticLocations);
|
||||||
|
this.customized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] appendSlashIfNecessary(String[] staticLocations) {
|
||||||
|
String[] normalized = new String[staticLocations.length];
|
||||||
|
for (int i = 0; i < staticLocations.length; i++) {
|
||||||
|
String location = staticLocations[i];
|
||||||
|
normalized[i] = location.endsWith("/") ? location : location + "/";
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddMappings() {
|
||||||
|
return this.addMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddMappings(boolean addMappings) {
|
||||||
|
this.customized = true;
|
||||||
|
this.addMappings = addMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chain getChain() {
|
||||||
|
return this.chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cache getCache() {
|
||||||
|
return this.cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasBeenCustomized() {
|
||||||
|
return this.customized || getChain().hasBeenCustomized() || getCache().hasBeenCustomized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for the Spring Resource Handling chain.
|
||||||
|
*/
|
||||||
|
public static class Chain {
|
||||||
|
|
||||||
|
boolean customized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable the Spring Resource Handling chain. By default, disabled
|
||||||
|
* unless at least one strategy has been enabled.
|
||||||
|
*/
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable caching in the Resource chain.
|
||||||
|
*/
|
||||||
|
private boolean cache = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable resolution of already compressed resources (gzip,
|
||||||
|
* brotli). Checks for a resource name with the '.gz' or '.br' file
|
||||||
|
* extensions.
|
||||||
|
*/
|
||||||
|
private boolean compressed = false;
|
||||||
|
|
||||||
|
private final Strategy strategy = new Strategy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the resource chain is enabled. Return {@code null} if no
|
||||||
|
* specific settings are present.
|
||||||
|
* @return whether the resource chain is enabled or {@code null} if no
|
||||||
|
* specified settings are present.
|
||||||
|
*/
|
||||||
|
public Boolean getEnabled() {
|
||||||
|
return getEnabled(getStrategy().getFixed().isEnabled(), getStrategy().getContent().isEnabled(),
|
||||||
|
this.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBeenCustomized() {
|
||||||
|
return this.customized || getStrategy().hasBeenCustomized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
this.customized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCache() {
|
||||||
|
return this.cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCache(boolean cache) {
|
||||||
|
this.cache = cache;
|
||||||
|
this.customized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Strategy getStrategy() {
|
||||||
|
return this.strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompressed() {
|
||||||
|
return this.compressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompressed(boolean compressed) {
|
||||||
|
this.compressed = compressed;
|
||||||
|
this.customized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Boolean getEnabled(boolean fixedEnabled, boolean contentEnabled, Boolean chainEnabled) {
|
||||||
|
return (fixedEnabled || contentEnabled) ? Boolean.TRUE : chainEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 this.fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Content getContent() {
|
||||||
|
return this.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBeenCustomized() {
|
||||||
|
return getFixed().hasBeenCustomized() || getContent().hasBeenCustomized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version Strategy based on content hashing.
|
||||||
|
*/
|
||||||
|
public static class Content {
|
||||||
|
|
||||||
|
private boolean customized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable the content Version Strategy.
|
||||||
|
*/
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comma-separated list of patterns to apply to the content Version
|
||||||
|
* Strategy.
|
||||||
|
*/
|
||||||
|
private String[] paths = new String[] { "/**" };
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.customized = true;
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getPaths() {
|
||||||
|
return this.paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaths(String[] paths) {
|
||||||
|
this.customized = true;
|
||||||
|
this.paths = paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBeenCustomized() {
|
||||||
|
return this.customized;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version Strategy based on a fixed version string.
|
||||||
|
*/
|
||||||
|
public static class Fixed {
|
||||||
|
|
||||||
|
private boolean customized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable the fixed Version Strategy.
|
||||||
|
*/
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comma-separated list of patterns to apply to the fixed Version
|
||||||
|
* Strategy.
|
||||||
|
*/
|
||||||
|
private String[] paths = new String[] { "/**" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version string to use for the fixed Version Strategy.
|
||||||
|
*/
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.customized = true;
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getPaths() {
|
||||||
|
return this.paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaths(String[] paths) {
|
||||||
|
this.customized = true;
|
||||||
|
this.paths = paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.customized = true;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBeenCustomized() {
|
||||||
|
return this.customized;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache configuration.
|
||||||
|
*/
|
||||||
|
public static class Cache {
|
||||||
|
|
||||||
|
private boolean customized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache period for the resources served by the resource handler. If a
|
||||||
|
* duration suffix is not specified, seconds will be used. Can be overridden
|
||||||
|
* by the 'spring.web.resources.cache.cachecontrol' properties.
|
||||||
|
*/
|
||||||
|
@DurationUnit(ChronoUnit.SECONDS)
|
||||||
|
private Duration period;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache control HTTP headers, only allows valid directive combinations.
|
||||||
|
* Overrides the 'spring.web.resources.cache.period' property.
|
||||||
|
*/
|
||||||
|
private final Cachecontrol cachecontrol = new Cachecontrol();
|
||||||
|
|
||||||
|
public Duration getPeriod() {
|
||||||
|
return this.period;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeriod(Duration period) {
|
||||||
|
this.customized = true;
|
||||||
|
this.period = period;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cachecontrol getCachecontrol() {
|
||||||
|
return this.cachecontrol;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBeenCustomized() {
|
||||||
|
return this.customized || getCachecontrol().hasBeenCustomized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache Control HTTP header configuration.
|
||||||
|
*/
|
||||||
|
public static class Cachecontrol {
|
||||||
|
|
||||||
|
private boolean customized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum time the response should be cached, in seconds if no duration
|
||||||
|
* suffix is not specified.
|
||||||
|
*/
|
||||||
|
@DurationUnit(ChronoUnit.SECONDS)
|
||||||
|
private Duration maxAge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the cached response can be reused only if re-validated
|
||||||
|
* with the server.
|
||||||
|
*/
|
||||||
|
private Boolean noCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate to not cache the response in any case.
|
||||||
|
*/
|
||||||
|
private Boolean noStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that once it has become stale, a cache must not use the
|
||||||
|
* response without re-validating it with the server.
|
||||||
|
*/
|
||||||
|
private Boolean mustRevalidate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate intermediaries (caches and others) that they should not
|
||||||
|
* transform the response content.
|
||||||
|
*/
|
||||||
|
private Boolean noTransform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that any cache may store the response.
|
||||||
|
*/
|
||||||
|
private Boolean cachePublic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the response message is intended for a single user and
|
||||||
|
* must not be stored by a shared cache.
|
||||||
|
*/
|
||||||
|
private Boolean cachePrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same meaning as the "must-revalidate" directive, except that it does
|
||||||
|
* not apply to private caches.
|
||||||
|
*/
|
||||||
|
private Boolean proxyRevalidate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum time the response can be served after it becomes stale, in
|
||||||
|
* seconds if no duration suffix is not specified.
|
||||||
|
*/
|
||||||
|
@DurationUnit(ChronoUnit.SECONDS)
|
||||||
|
private Duration staleWhileRevalidate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum time the response may be used when errors are encountered, in
|
||||||
|
* seconds if no duration suffix is not specified.
|
||||||
|
*/
|
||||||
|
@DurationUnit(ChronoUnit.SECONDS)
|
||||||
|
private Duration staleIfError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum time the response should be cached by shared caches, in seconds
|
||||||
|
* if no duration suffix is not specified.
|
||||||
|
*/
|
||||||
|
@DurationUnit(ChronoUnit.SECONDS)
|
||||||
|
private Duration sMaxAge;
|
||||||
|
|
||||||
|
public Duration getMaxAge() {
|
||||||
|
return this.maxAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxAge(Duration maxAge) {
|
||||||
|
this.customized = true;
|
||||||
|
this.maxAge = maxAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getNoCache() {
|
||||||
|
return this.noCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNoCache(Boolean noCache) {
|
||||||
|
this.customized = true;
|
||||||
|
this.noCache = noCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getNoStore() {
|
||||||
|
return this.noStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNoStore(Boolean noStore) {
|
||||||
|
this.customized = true;
|
||||||
|
this.noStore = noStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getMustRevalidate() {
|
||||||
|
return this.mustRevalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMustRevalidate(Boolean mustRevalidate) {
|
||||||
|
this.customized = true;
|
||||||
|
this.mustRevalidate = mustRevalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getNoTransform() {
|
||||||
|
return this.noTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNoTransform(Boolean noTransform) {
|
||||||
|
this.customized = true;
|
||||||
|
this.noTransform = noTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getCachePublic() {
|
||||||
|
return this.cachePublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCachePublic(Boolean cachePublic) {
|
||||||
|
this.customized = true;
|
||||||
|
this.cachePublic = cachePublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getCachePrivate() {
|
||||||
|
return this.cachePrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCachePrivate(Boolean cachePrivate) {
|
||||||
|
this.customized = true;
|
||||||
|
this.cachePrivate = cachePrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getProxyRevalidate() {
|
||||||
|
return this.proxyRevalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyRevalidate(Boolean proxyRevalidate) {
|
||||||
|
this.customized = true;
|
||||||
|
this.proxyRevalidate = proxyRevalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getStaleWhileRevalidate() {
|
||||||
|
return this.staleWhileRevalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStaleWhileRevalidate(Duration staleWhileRevalidate) {
|
||||||
|
this.customized = true;
|
||||||
|
this.staleWhileRevalidate = staleWhileRevalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getStaleIfError() {
|
||||||
|
return this.staleIfError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStaleIfError(Duration staleIfError) {
|
||||||
|
this.customized = true;
|
||||||
|
this.staleIfError = staleIfError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getSMaxAge() {
|
||||||
|
return this.sMaxAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSMaxAge(Duration sMaxAge) {
|
||||||
|
this.customized = true;
|
||||||
|
this.sMaxAge = sMaxAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheControl toHttpCacheControl() {
|
||||||
|
PropertyMapper map = PropertyMapper.get();
|
||||||
|
CacheControl control = createCacheControl();
|
||||||
|
map.from(this::getMustRevalidate).whenTrue().toCall(control::mustRevalidate);
|
||||||
|
map.from(this::getNoTransform).whenTrue().toCall(control::noTransform);
|
||||||
|
map.from(this::getCachePublic).whenTrue().toCall(control::cachePublic);
|
||||||
|
map.from(this::getCachePrivate).whenTrue().toCall(control::cachePrivate);
|
||||||
|
map.from(this::getProxyRevalidate).whenTrue().toCall(control::proxyRevalidate);
|
||||||
|
map.from(this::getStaleWhileRevalidate).whenNonNull()
|
||||||
|
.to((duration) -> control.staleWhileRevalidate(duration.getSeconds(), TimeUnit.SECONDS));
|
||||||
|
map.from(this::getStaleIfError).whenNonNull()
|
||||||
|
.to((duration) -> control.staleIfError(duration.getSeconds(), TimeUnit.SECONDS));
|
||||||
|
map.from(this::getSMaxAge).whenNonNull()
|
||||||
|
.to((duration) -> control.sMaxAge(duration.getSeconds(), TimeUnit.SECONDS));
|
||||||
|
// check if cacheControl remained untouched
|
||||||
|
if (control.getHeaderValue() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CacheControl createCacheControl() {
|
||||||
|
if (Boolean.TRUE.equals(this.noStore)) {
|
||||||
|
return CacheControl.noStore();
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(this.noCache)) {
|
||||||
|
return CacheControl.noCache();
|
||||||
|
}
|
||||||
|
if (this.maxAge != null) {
|
||||||
|
return CacheControl.maxAge(this.maxAge.getSeconds(), TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
return CacheControl.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBeenCustomized() {
|
||||||
|
return this.customized;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.web.reactive;
|
package org.springframework.boot.autoconfigure.web.reactive;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
|
||||||
import org.springframework.web.reactive.config.ResourceChainRegistration;
|
import org.springframework.web.reactive.config.ResourceChainRegistration;
|
||||||
import org.springframework.web.reactive.config.ResourceHandlerRegistration;
|
import org.springframework.web.reactive.config.ResourceHandlerRegistration;
|
||||||
import org.springframework.web.reactive.resource.EncodedResourceResolver;
|
import org.springframework.web.reactive.resource.EncodedResourceResolver;
|
||||||
|
|
@ -32,30 +31,35 @@ import org.springframework.web.reactive.resource.VersionResourceResolver;
|
||||||
*/
|
*/
|
||||||
class ResourceChainResourceHandlerRegistrationCustomizer implements ResourceHandlerRegistrationCustomizer {
|
class ResourceChainResourceHandlerRegistrationCustomizer implements ResourceHandlerRegistrationCustomizer {
|
||||||
|
|
||||||
@Autowired
|
private final Resources resourceProperties;
|
||||||
private ResourceProperties resourceProperties = new ResourceProperties();
|
|
||||||
|
ResourceChainResourceHandlerRegistrationCustomizer(Resources resources) {
|
||||||
|
this.resourceProperties = resources;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customize(ResourceHandlerRegistration registration) {
|
public void customize(ResourceHandlerRegistration registration) {
|
||||||
ResourceProperties.Chain properties = this.resourceProperties.getChain();
|
Resources.Chain properties = this.resourceProperties.getChain();
|
||||||
configureResourceChain(properties, registration.resourceChain(properties.isCache()));
|
configureResourceChain(properties, registration.resourceChain(properties.isCache()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void configureResourceChain(ResourceProperties.Chain properties, ResourceChainRegistration chain) {
|
private void configureResourceChain(Resources.Chain properties, ResourceChainRegistration chain) {
|
||||||
ResourceProperties.Strategy strategy = properties.getStrategy();
|
Resources.Chain.Strategy strategy = properties.getStrategy();
|
||||||
if (properties.isCompressed()) {
|
if (properties.isCompressed()) {
|
||||||
chain.addResolver(new EncodedResourceResolver());
|
chain.addResolver(new EncodedResourceResolver());
|
||||||
}
|
}
|
||||||
if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
|
if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
|
||||||
chain.addResolver(getVersionResourceResolver(strategy));
|
chain.addResolver(getVersionResourceResolver(strategy));
|
||||||
}
|
}
|
||||||
if (properties.isHtmlApplicationCache()) {
|
if ((properties instanceof org.springframework.boot.autoconfigure.web.ResourceProperties.Chain)
|
||||||
|
&& ((org.springframework.boot.autoconfigure.web.ResourceProperties.Chain) properties)
|
||||||
|
.isHtmlApplicationCache()) {
|
||||||
chain.addTransformer(new org.springframework.web.reactive.resource.AppCacheManifestTransformer());
|
chain.addTransformer(new org.springframework.web.reactive.resource.AppCacheManifestTransformer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResourceResolver getVersionResourceResolver(ResourceProperties.Strategy properties) {
|
private ResourceResolver getVersionResourceResolver(Resources.Chain.Strategy properties) {
|
||||||
VersionResourceResolver resolver = new VersionResourceResolver();
|
VersionResourceResolver resolver = new VersionResourceResolver();
|
||||||
if (properties.getFixed().isEnabled()) {
|
if (properties.getFixed().isEnabled()) {
|
||||||
String version = properties.getFixed().getVersion();
|
String version = properties.getFixed().getVersion();
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvi
|
||||||
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
|
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
|
||||||
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
|
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
|
||||||
import org.springframework.boot.autoconfigure.web.WebProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.autoconfigure.web.format.DateTimeFormatters;
|
import org.springframework.boot.autoconfigure.web.format.DateTimeFormatters;
|
||||||
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
||||||
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties.Format;
|
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties.Format;
|
||||||
|
|
@ -106,11 +106,16 @@ public class WebFluxAutoConfiguration {
|
||||||
public static class WelcomePageConfiguration {
|
public static class WelcomePageConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public RouterFunctionMapping welcomePageRouterFunctionMapping(ApplicationContext applicationContext,
|
public RouterFunctionMapping welcomePageRouterFunctionMapping(ApplicationContext applicationContext,
|
||||||
WebFluxProperties webFluxProperties, ResourceProperties resourceProperties) {
|
WebFluxProperties webFluxProperties,
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
|
WebProperties webProperties) {
|
||||||
|
String[] staticLocations = resourceProperties.hasBeenCustomized() ? resourceProperties.getStaticLocations()
|
||||||
|
: webProperties.getResources().getStaticLocations();
|
||||||
WelcomePageRouterFunctionFactory factory = new WelcomePageRouterFunctionFactory(
|
WelcomePageRouterFunctionFactory factory = new WelcomePageRouterFunctionFactory(
|
||||||
new TemplateAvailabilityProviders(applicationContext), applicationContext,
|
new TemplateAvailabilityProviders(applicationContext), applicationContext, staticLocations,
|
||||||
resourceProperties.getStaticLocations(), webFluxProperties.getStaticPathPattern());
|
webFluxProperties.getStaticPathPattern());
|
||||||
RouterFunction<ServerResponse> routerFunction = factory.createRouterFunction();
|
RouterFunction<ServerResponse> routerFunction = factory.createRouterFunction();
|
||||||
if (routerFunction != null) {
|
if (routerFunction != null) {
|
||||||
RouterFunctionMapping routerFunctionMapping = new RouterFunctionMapping(routerFunction);
|
RouterFunctionMapping routerFunctionMapping = new RouterFunctionMapping(routerFunction);
|
||||||
|
|
@ -122,14 +127,16 @@ public class WebFluxAutoConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableConfigurationProperties({ ResourceProperties.class, WebFluxProperties.class })
|
@EnableConfigurationProperties({ org.springframework.boot.autoconfigure.web.ResourceProperties.class,
|
||||||
|
WebProperties.class, WebFluxProperties.class })
|
||||||
@Import({ EnableWebFluxConfiguration.class })
|
@Import({ EnableWebFluxConfiguration.class })
|
||||||
public static class WebFluxConfig implements WebFluxConfigurer {
|
public static class WebFluxConfig implements WebFluxConfigurer {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(WebFluxConfig.class);
|
private static final Log logger = LogFactory.getLog(WebFluxConfig.class);
|
||||||
|
|
||||||
private final ResourceProperties resourceProperties;
|
private final Resources resourceProperties;
|
||||||
|
|
||||||
private final WebFluxProperties webFluxProperties;
|
private final WebFluxProperties webFluxProperties;
|
||||||
|
|
||||||
|
|
@ -143,12 +150,14 @@ public class WebFluxAutoConfiguration {
|
||||||
|
|
||||||
private final ObjectProvider<ViewResolver> viewResolvers;
|
private final ObjectProvider<ViewResolver> viewResolvers;
|
||||||
|
|
||||||
public WebFluxConfig(ResourceProperties resourceProperties, WebFluxProperties webFluxProperties,
|
public WebFluxConfig(org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
ListableBeanFactory beanFactory, ObjectProvider<HandlerMethodArgumentResolver> resolvers,
|
WebProperties webProperties, WebFluxProperties webFluxProperties, ListableBeanFactory beanFactory,
|
||||||
|
ObjectProvider<HandlerMethodArgumentResolver> resolvers,
|
||||||
ObjectProvider<CodecCustomizer> codecCustomizers,
|
ObjectProvider<CodecCustomizer> codecCustomizers,
|
||||||
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizer,
|
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizer,
|
||||||
ObjectProvider<ViewResolver> viewResolvers) {
|
ObjectProvider<ViewResolver> viewResolvers) {
|
||||||
this.resourceProperties = resourceProperties;
|
this.resourceProperties = resourceProperties.hasBeenCustomized() ? resourceProperties
|
||||||
|
: webProperties.getResources();
|
||||||
this.webFluxProperties = webFluxProperties;
|
this.webFluxProperties = webFluxProperties;
|
||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
this.argumentResolvers = resolvers;
|
this.argumentResolvers = resolvers;
|
||||||
|
|
@ -190,7 +199,8 @@ public class WebFluxAutoConfiguration {
|
||||||
|
|
||||||
private void configureResourceCaching(ResourceHandlerRegistration registration) {
|
private void configureResourceCaching(ResourceHandlerRegistration registration) {
|
||||||
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
|
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
|
||||||
ResourceProperties.Cache.Cachecontrol cacheControl = this.resourceProperties.getCache().getCachecontrol();
|
WebProperties.Resources.Cache.Cachecontrol cacheControl = this.resourceProperties.getCache()
|
||||||
|
.getCachecontrol();
|
||||||
if (cachePeriod != null && cacheControl.getMaxAge() == null) {
|
if (cachePeriod != null && cacheControl.getMaxAge() == null) {
|
||||||
cacheControl.setMaxAge(cachePeriod);
|
cacheControl.setMaxAge(cachePeriod);
|
||||||
}
|
}
|
||||||
|
|
@ -291,8 +301,13 @@ public class WebFluxAutoConfiguration {
|
||||||
static class ResourceChainCustomizerConfiguration {
|
static class ResourceChainCustomizerConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
|
@SuppressWarnings("deprecation")
|
||||||
return new ResourceChainResourceHandlerRegistrationCustomizer();
|
ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer(
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
|
WebProperties webProperties) {
|
||||||
|
Resources resources = resourceProperties.hasBeenCustomized() ? resourceProperties
|
||||||
|
: webProperties.getResources();
|
||||||
|
return new ResourceChainResourceHandlerRegistrationCustomizer(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||||
|
|
@ -82,7 +82,7 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
||||||
|
|
||||||
private final ErrorAttributes errorAttributes;
|
private final ErrorAttributes errorAttributes;
|
||||||
|
|
||||||
private final ResourceProperties resourceProperties;
|
private final Resources resources;
|
||||||
|
|
||||||
private final TemplateAvailabilityProviders templateAvailabilityProviders;
|
private final TemplateAvailabilityProviders templateAvailabilityProviders;
|
||||||
|
|
||||||
|
|
@ -92,13 +92,35 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
||||||
|
|
||||||
private List<ViewResolver> viewResolvers = Collections.emptyList();
|
private List<ViewResolver> viewResolvers = Collections.emptyList();
|
||||||
|
|
||||||
public AbstractErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
|
/**
|
||||||
|
* Create a new {@code AbstractErrorWebExceptionHandler}.
|
||||||
|
* @param errorAttributes the error attributes
|
||||||
|
* @param resourceProperties the resource properties
|
||||||
|
* @param applicationContext the application context
|
||||||
|
* @deprecated since 2.4.0 in favor of
|
||||||
|
* {@link #AbstractErrorWebExceptionHandler(ErrorAttributes, Resources, ApplicationContext)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public AbstractErrorWebExceptionHandler(ErrorAttributes errorAttributes,
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
|
ApplicationContext applicationContext) {
|
||||||
|
this(errorAttributes, (Resources) resourceProperties, applicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code AbstractErrorWebExceptionHandler}.
|
||||||
|
* @param errorAttributes the error attributes
|
||||||
|
* @param resources the resources configuration properties
|
||||||
|
* @param applicationContext the application context
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public AbstractErrorWebExceptionHandler(ErrorAttributes errorAttributes, Resources resources,
|
||||||
ApplicationContext applicationContext) {
|
ApplicationContext applicationContext) {
|
||||||
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
|
Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
|
||||||
Assert.notNull(resourceProperties, "ResourceProperties must not be null");
|
Assert.notNull(resources, "Resources must not be null");
|
||||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||||
this.errorAttributes = errorAttributes;
|
this.errorAttributes = errorAttributes;
|
||||||
this.resourceProperties = resourceProperties;
|
this.resources = resources;
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.templateAvailabilityProviders = new TemplateAvailabilityProviders(applicationContext);
|
this.templateAvailabilityProviders = new TemplateAvailabilityProviders(applicationContext);
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +246,7 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
||||||
}
|
}
|
||||||
|
|
||||||
private Resource resolveResource(String viewName) {
|
private Resource resolveResource(String viewName) {
|
||||||
for (String location : this.resourceProperties.getStaticLocations()) {
|
for (String location : this.resources.getStaticLocations()) {
|
||||||
try {
|
try {
|
||||||
Resource resource = this.applicationContext.getResource(location);
|
Resource resource = this.applicationContext.getResource(location);
|
||||||
resource = resource.createRelative(viewName + ".html");
|
resource = resource.createRelative(viewName + ".html");
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||||
|
|
@ -97,10 +97,27 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
||||||
* @param resourceProperties the resources configuration properties
|
* @param resourceProperties the resources configuration properties
|
||||||
* @param errorProperties the error configuration properties
|
* @param errorProperties the error configuration properties
|
||||||
* @param applicationContext the current application context
|
* @param applicationContext the current application context
|
||||||
|
* @deprecated since 2.4.0 in favor of
|
||||||
|
* {@link #DefaultErrorWebExceptionHandler(ErrorAttributes, Resources, ErrorProperties, ApplicationContext)}
|
||||||
*/
|
*/
|
||||||
public DefaultErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
|
@Deprecated
|
||||||
|
public DefaultErrorWebExceptionHandler(ErrorAttributes errorAttributes,
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
ErrorProperties errorProperties, ApplicationContext applicationContext) {
|
ErrorProperties errorProperties, ApplicationContext applicationContext) {
|
||||||
super(errorAttributes, resourceProperties, applicationContext);
|
this(errorAttributes, (Resources) resourceProperties, errorProperties, applicationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code DefaultErrorWebExceptionHandler} instance.
|
||||||
|
* @param errorAttributes the error attributes
|
||||||
|
* @param resources the resources configuration properties
|
||||||
|
* @param errorProperties the error configuration properties
|
||||||
|
* @param applicationContext the current application context
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public DefaultErrorWebExceptionHandler(ErrorAttributes errorAttributes, Resources resources,
|
||||||
|
ErrorProperties errorProperties, ApplicationContext applicationContext) {
|
||||||
|
super(errorAttributes, resources, applicationContext);
|
||||||
this.errorProperties = errorProperties;
|
this.errorProperties = errorProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||||
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
|
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
|
||||||
|
|
@ -48,11 +48,13 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
||||||
* @author Scott Frederick
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||||
@ConditionalOnClass(WebFluxConfigurer.class)
|
@ConditionalOnClass(WebFluxConfigurer.class)
|
||||||
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
|
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
|
||||||
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class })
|
@EnableConfigurationProperties({ ServerProperties.class,
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
|
||||||
public class ErrorWebFluxAutoConfiguration {
|
public class ErrorWebFluxAutoConfiguration {
|
||||||
|
|
||||||
private final ServerProperties serverProperties;
|
private final ServerProperties serverProperties;
|
||||||
|
|
@ -65,10 +67,12 @@ public class ErrorWebFluxAutoConfiguration {
|
||||||
@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
|
@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
|
||||||
@Order(-1)
|
@Order(-1)
|
||||||
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,
|
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,
|
||||||
ResourceProperties resourceProperties, ObjectProvider<ViewResolver> viewResolvers,
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
|
WebProperties webProperties, ObjectProvider<ViewResolver> viewResolvers,
|
||||||
ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {
|
ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {
|
||||||
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
|
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
|
||||||
resourceProperties, this.serverProperties.getError(), applicationContext);
|
resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources(),
|
||||||
|
this.serverProperties.getError(), applicationContext);
|
||||||
exceptionHandler.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()));
|
exceptionHandler.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()));
|
||||||
exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
|
exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
|
||||||
exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
|
exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||||
|
|
@ -50,9 +49,9 @@ import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvi
|
||||||
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
|
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
|
||||||
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
|
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
|
|
||||||
import org.springframework.boot.autoconfigure.web.WebProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources.Chain.Strategy;
|
||||||
import org.springframework.boot.autoconfigure.web.format.DateTimeFormatters;
|
import org.springframework.boot.autoconfigure.web.format.DateTimeFormatters;
|
||||||
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
||||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.Format;
|
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.Format;
|
||||||
|
|
@ -175,15 +174,17 @@ public class WebMvcAutoConfiguration {
|
||||||
|
|
||||||
// Defined as a nested config to ensure WebMvcConfigurer is not read when not
|
// Defined as a nested config to ensure WebMvcConfigurer is not read when not
|
||||||
// on the classpath
|
// on the classpath
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Import(EnableWebMvcConfiguration.class)
|
@Import(EnableWebMvcConfiguration.class)
|
||||||
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
|
@EnableConfigurationProperties({ WebMvcProperties.class,
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
|
||||||
@Order(0)
|
@Order(0)
|
||||||
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
|
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
|
private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
|
||||||
|
|
||||||
private final ResourceProperties resourceProperties;
|
private final Resources resourceProperties;
|
||||||
|
|
||||||
private final WebMvcProperties mvcProperties;
|
private final WebMvcProperties mvcProperties;
|
||||||
|
|
||||||
|
|
@ -197,12 +198,15 @@ public class WebMvcAutoConfiguration {
|
||||||
|
|
||||||
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
|
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
|
||||||
|
|
||||||
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
|
public WebMvcAutoConfigurationAdapter(
|
||||||
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
|
WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
|
||||||
|
ObjectProvider<HttpMessageConverters> messageConvertersProvider,
|
||||||
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
|
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
|
||||||
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
|
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
|
||||||
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
|
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
|
||||||
this.resourceProperties = resourceProperties;
|
this.resourceProperties = resourceProperties.hasBeenCustomized() ? resourceProperties
|
||||||
|
: webProperties.getResources();
|
||||||
this.mvcProperties = mvcProperties;
|
this.mvcProperties = mvcProperties;
|
||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
this.messageConvertersProvider = messageConvertersProvider;
|
this.messageConvertersProvider = messageConvertersProvider;
|
||||||
|
|
@ -234,7 +238,6 @@ public class WebMvcAutoConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
public void configurePathMatch(PathMatchConfigurer configurer) {
|
||||||
if (this.mvcProperties.getPathmatch()
|
if (this.mvcProperties.getPathmatch()
|
||||||
.getMatchingStrategy() == WebMvcProperties.MatchingStrategy.PATH_PATTERN_PARSER) {
|
.getMatchingStrategy() == WebMvcProperties.MatchingStrategy.PATH_PATTERN_PARSER) {
|
||||||
|
|
@ -259,7 +262,6 @@ public class WebMvcAutoConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
||||||
WebMvcProperties.Contentnegotiation contentnegotiation = this.mvcProperties.getContentnegotiation();
|
WebMvcProperties.Contentnegotiation contentnegotiation = this.mvcProperties.getContentnegotiation();
|
||||||
configurer.favorPathExtension(contentnegotiation.isFavorPathExtension());
|
configurer.favorPathExtension(contentnegotiation.isFavorPathExtension());
|
||||||
|
|
@ -363,7 +365,7 @@ public class WebMvcAutoConfiguration {
|
||||||
@EnableConfigurationProperties(WebProperties.class)
|
@EnableConfigurationProperties(WebProperties.class)
|
||||||
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
|
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
|
||||||
|
|
||||||
private final ResourceProperties resourceProperties;
|
private final Resources resourceProperties;
|
||||||
|
|
||||||
private final WebMvcProperties mvcProperties;
|
private final WebMvcProperties mvcProperties;
|
||||||
|
|
||||||
|
|
@ -375,10 +377,13 @@ public class WebMvcAutoConfiguration {
|
||||||
|
|
||||||
private ResourceLoader resourceLoader;
|
private ResourceLoader resourceLoader;
|
||||||
|
|
||||||
public EnableWebMvcConfiguration(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
|
@SuppressWarnings("deprecation")
|
||||||
WebProperties webProperties, ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
|
public EnableWebMvcConfiguration(
|
||||||
ListableBeanFactory beanFactory) {
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
this.resourceProperties = resourceProperties;
|
WebMvcProperties mvcProperties, WebProperties webProperties,
|
||||||
|
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ListableBeanFactory beanFactory) {
|
||||||
|
this.resourceProperties = resourceProperties.hasBeenCustomized() ? resourceProperties
|
||||||
|
: webProperties.getResources();
|
||||||
this.mvcProperties = mvcProperties;
|
this.mvcProperties = mvcProperties;
|
||||||
this.webProperties = webProperties;
|
this.webProperties = webProperties;
|
||||||
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
|
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
|
||||||
|
|
@ -551,8 +556,12 @@ public class WebMvcAutoConfiguration {
|
||||||
static class ResourceChainCustomizerConfiguration {
|
static class ResourceChainCustomizerConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
|
@SuppressWarnings("deprecation")
|
||||||
return new ResourceChainResourceHandlerRegistrationCustomizer();
|
ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer(
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
|
WebProperties webProperties) {
|
||||||
|
return new ResourceChainResourceHandlerRegistrationCustomizer(
|
||||||
|
resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -565,17 +574,20 @@ public class WebMvcAutoConfiguration {
|
||||||
|
|
||||||
static class ResourceChainResourceHandlerRegistrationCustomizer implements ResourceHandlerRegistrationCustomizer {
|
static class ResourceChainResourceHandlerRegistrationCustomizer implements ResourceHandlerRegistrationCustomizer {
|
||||||
|
|
||||||
@Autowired
|
private final Resources resourceProperties;
|
||||||
private ResourceProperties resourceProperties = new ResourceProperties();
|
|
||||||
|
ResourceChainResourceHandlerRegistrationCustomizer(Resources resourceProperties) {
|
||||||
|
this.resourceProperties = resourceProperties;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customize(ResourceHandlerRegistration registration) {
|
public void customize(ResourceHandlerRegistration registration) {
|
||||||
ResourceProperties.Chain properties = this.resourceProperties.getChain();
|
Resources.Chain properties = this.resourceProperties.getChain();
|
||||||
configureResourceChain(properties, registration.resourceChain(properties.isCache()));
|
configureResourceChain(properties, registration.resourceChain(properties.isCache()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void configureResourceChain(ResourceProperties.Chain properties, ResourceChainRegistration chain) {
|
private void configureResourceChain(Resources.Chain properties, ResourceChainRegistration chain) {
|
||||||
Strategy strategy = properties.getStrategy();
|
Strategy strategy = properties.getStrategy();
|
||||||
if (properties.isCompressed()) {
|
if (properties.isCompressed()) {
|
||||||
chain.addResolver(new EncodedResourceResolver());
|
chain.addResolver(new EncodedResourceResolver());
|
||||||
|
|
@ -583,12 +595,14 @@ public class WebMvcAutoConfiguration {
|
||||||
if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
|
if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
|
||||||
chain.addResolver(getVersionResourceResolver(strategy));
|
chain.addResolver(getVersionResourceResolver(strategy));
|
||||||
}
|
}
|
||||||
if (properties.isHtmlApplicationCache()) {
|
if (properties instanceof org.springframework.boot.autoconfigure.web.ResourceProperties.Chain
|
||||||
|
&& ((org.springframework.boot.autoconfigure.web.ResourceProperties.Chain) properties)
|
||||||
|
.isHtmlApplicationCache()) {
|
||||||
chain.addTransformer(new org.springframework.web.servlet.resource.AppCacheManifestTransformer());
|
chain.addTransformer(new org.springframework.web.servlet.resource.AppCacheManifestTransformer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResourceResolver getVersionResourceResolver(ResourceProperties.Strategy properties) {
|
private ResourceResolver getVersionResourceResolver(Strategy properties) {
|
||||||
VersionResourceResolver resolver = new VersionResourceResolver();
|
VersionResourceResolver resolver = new VersionResourceResolver();
|
||||||
if (properties.getFixed().isEnabled()) {
|
if (properties.getFixed().isEnabled()) {
|
||||||
String version = properties.getFixed().getVersion();
|
String version = properties.getFixed().getVersion();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider;
|
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider;
|
||||||
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
@ -68,7 +68,7 @@ public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
private final ResourceProperties resourceProperties;
|
private final Resources resources;
|
||||||
|
|
||||||
private final TemplateAvailabilityProviders templateAvailabilityProviders;
|
private final TemplateAvailabilityProviders templateAvailabilityProviders;
|
||||||
|
|
||||||
|
|
@ -78,21 +78,35 @@ public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
|
||||||
* Create a new {@link DefaultErrorViewResolver} instance.
|
* Create a new {@link DefaultErrorViewResolver} instance.
|
||||||
* @param applicationContext the source application context
|
* @param applicationContext the source application context
|
||||||
* @param resourceProperties resource properties
|
* @param resourceProperties resource properties
|
||||||
|
* @deprecated since 2.4.0 in favour of
|
||||||
|
* {@link #DefaultErrorViewResolver(ApplicationContext, Resources)}
|
||||||
*/
|
*/
|
||||||
public DefaultErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {
|
@Deprecated
|
||||||
|
public DefaultErrorViewResolver(ApplicationContext applicationContext,
|
||||||
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties) {
|
||||||
|
this(applicationContext, (Resources) resourceProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link DefaultErrorViewResolver} instance.
|
||||||
|
* @param applicationContext the source application context
|
||||||
|
* @param resources resource properties
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public DefaultErrorViewResolver(ApplicationContext applicationContext, Resources resources) {
|
||||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||||
Assert.notNull(resourceProperties, "ResourceProperties must not be null");
|
Assert.notNull(resources, "Resources must not be null");
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.resourceProperties = resourceProperties;
|
this.resources = resources;
|
||||||
this.templateAvailabilityProviders = new TemplateAvailabilityProviders(applicationContext);
|
this.templateAvailabilityProviders = new TemplateAvailabilityProviders(applicationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties,
|
DefaultErrorViewResolver(ApplicationContext applicationContext, Resources resourceProperties,
|
||||||
TemplateAvailabilityProviders templateAvailabilityProviders) {
|
TemplateAvailabilityProviders templateAvailabilityProviders) {
|
||||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||||
Assert.notNull(resourceProperties, "ResourceProperties must not be null");
|
Assert.notNull(resourceProperties, "Resources must not be null");
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.resourceProperties = resourceProperties;
|
this.resources = resourceProperties;
|
||||||
this.templateAvailabilityProviders = templateAvailabilityProviders;
|
this.templateAvailabilityProviders = templateAvailabilityProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +130,7 @@ public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
|
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
|
||||||
for (String location : this.resourceProperties.getStaticLocations()) {
|
for (String location : this.resources.getStaticLocations()) {
|
||||||
try {
|
try {
|
||||||
Resource resource = this.applicationContext.getResource(location);
|
Resource resource = this.applicationContext.getResource(location);
|
||||||
resource = resource.createRelative(viewName + ".html");
|
resource = resource.createRelative(viewName + ".html");
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,9 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||||
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider;
|
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider;
|
||||||
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
|
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
|
||||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
|
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
|
||||||
|
|
@ -88,7 +89,7 @@ import org.springframework.web.util.HtmlUtils;
|
||||||
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
|
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
|
||||||
// Load before the main WebMvcAutoConfiguration so that the error View is available
|
// Load before the main WebMvcAutoConfiguration so that the error View is available
|
||||||
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
|
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
|
||||||
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })
|
@EnableConfigurationProperties({ ServerProperties.class, WebMvcProperties.class })
|
||||||
public class ErrorMvcAutoConfiguration {
|
public class ErrorMvcAutoConfiguration {
|
||||||
|
|
||||||
private final ServerProperties serverProperties;
|
private final ServerProperties serverProperties;
|
||||||
|
|
@ -121,24 +122,29 @@ public class ErrorMvcAutoConfiguration {
|
||||||
return new PreserveErrorControllerTargetClassPostProcessor();
|
return new PreserveErrorControllerTargetClassPostProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableConfigurationProperties({ org.springframework.boot.autoconfigure.web.ResourceProperties.class,
|
||||||
|
WebProperties.class, WebMvcProperties.class })
|
||||||
static class DefaultErrorViewResolverConfiguration {
|
static class DefaultErrorViewResolverConfiguration {
|
||||||
|
|
||||||
private final ApplicationContext applicationContext;
|
private final ApplicationContext applicationContext;
|
||||||
|
|
||||||
private final ResourceProperties resourceProperties;
|
private final Resources resources;
|
||||||
|
|
||||||
DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,
|
DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,
|
||||||
ResourceProperties resourceProperties) {
|
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
|
||||||
|
WebProperties webProperties) {
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.resourceProperties = resourceProperties;
|
this.resources = webProperties.getResources().hasBeenCustomized() ? webProperties.getResources()
|
||||||
|
: resourceProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnBean(DispatcherServlet.class)
|
@ConditionalOnBean(DispatcherServlet.class)
|
||||||
@ConditionalOnMissingBean(ErrorViewResolver.class)
|
@ConditionalOnMissingBean(ErrorViewResolver.class)
|
||||||
DefaultErrorViewResolver conventionErrorViewResolver() {
|
DefaultErrorViewResolver conventionErrorViewResolver() {
|
||||||
return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
|
return new DefaultErrorViewResolver(this.applicationContext, this.resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.web;
|
||||||
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import org.springframework.boot.test.util.TestPropertyValues;
|
import org.springframework.boot.test.util.TestPropertyValues;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
|
@ -46,27 +48,31 @@ class ConditionalOnEnabledResourceChainTests {
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(this.context.containsBean("foo")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void disabledExplicitly() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
load("spring.resources.chain.enabled:false");
|
void disabledExplicitly(String prefix) {
|
||||||
|
load(prefix + "chain.enabled:false");
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(this.context.containsBean("foo")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void enabledViaMainEnabledFlag() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
load("spring.resources.chain.enabled:true");
|
void enabledViaMainEnabledFlag(String prefix) {
|
||||||
|
load(prefix + "chain.enabled:true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(this.context.containsBean("foo")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void enabledViaFixedStrategyFlag() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
load("spring.resources.chain.strategy.fixed.enabled:true");
|
void enabledViaFixedStrategyFlag(String prefix) {
|
||||||
|
load(prefix + "chain.strategy.fixed.enabled:true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(this.context.containsBean("foo")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void enabledViaContentStrategyFlag() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
load("spring.resources.chain.strategy.content.enabled:true");
|
void enabledViaContentStrategyFlag(String prefix) {
|
||||||
|
load(prefix + "chain.strategy.content.enabled:true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(this.context.containsBean("foo")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
class ResourcePropertiesBindingTests {
|
class ResourcePropertiesBindingTests {
|
||||||
|
|
||||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||||
|
|
@ -60,6 +61,7 @@ class ResourcePropertiesBindingTests {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableConfigurationProperties(ResourceProperties.class)
|
@EnableConfigurationProperties(ResourceProperties.class)
|
||||||
static class TestConfiguration {
|
static class TestConfiguration {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import java.time.Duration;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties.Cache;
|
|
||||||
import org.springframework.http.CacheControl;
|
import org.springframework.http.CacheControl;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
@ -31,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Kristine Jetzke
|
* @author Kristine Jetzke
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
class ResourcePropertiesTests {
|
class ResourcePropertiesTests {
|
||||||
|
|
||||||
private final ResourceProperties properties = new ResourceProperties();
|
private final ResourceProperties properties = new ResourceProperties();
|
||||||
|
|
@ -78,7 +78,7 @@ class ResourcePropertiesTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cacheControlAllPropertiesSet() {
|
void cacheControlAllPropertiesSet() {
|
||||||
Cache.Cachecontrol properties = this.properties.getCache().getCachecontrol();
|
ResourceProperties.Cache.Cachecontrol properties = this.properties.getCache().getCachecontrol();
|
||||||
properties.setMaxAge(Duration.ofSeconds(4));
|
properties.setMaxAge(Duration.ofSeconds(4));
|
||||||
properties.setCachePrivate(true);
|
properties.setCachePrivate(true);
|
||||||
properties.setCachePublic(true);
|
properties.setCachePublic(true);
|
||||||
|
|
@ -96,7 +96,7 @@ class ResourcePropertiesTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void invalidCacheControlCombination() {
|
void invalidCacheControlCombination() {
|
||||||
Cache.Cachecontrol properties = this.properties.getCache().getCachecontrol();
|
ResourceProperties.Cache.Cachecontrol properties = this.properties.getCache().getCachecontrol();
|
||||||
properties.setMaxAge(Duration.ofSeconds(4));
|
properties.setMaxAge(Duration.ofSeconds(4));
|
||||||
properties.setNoStore(true);
|
properties.setNoStore(true);
|
||||||
CacheControl cacheControl = properties.toHttpCacheControl();
|
CacheControl cacheControl = properties.toHttpCacheControl();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.web;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binding tests for {@link WebProperties.Resources}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class WebPropertiesResourcesBindingTests {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||||
|
.withUserConfiguration(TestConfiguration.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void staticLocationsExpandArray() {
|
||||||
|
this.contextRunner
|
||||||
|
.withPropertyValues("spring.web.resources.static-locations[0]=classpath:/one/",
|
||||||
|
"spring.web.resources.static-locations[1]=classpath:/two",
|
||||||
|
"spring.web.resources.static-locations[2]=classpath:/three/",
|
||||||
|
"spring.web.resources.static-locations[3]=classpath:/four",
|
||||||
|
"spring.web.resources.static-locations[4]=classpath:/five/",
|
||||||
|
"spring.web.resources.static-locations[5]=classpath:/six")
|
||||||
|
.run(assertResourceProperties((properties) -> assertThat(properties.getStaticLocations()).contains(
|
||||||
|
"classpath:/one/", "classpath:/two/", "classpath:/three/", "classpath:/four/",
|
||||||
|
"classpath:/five/", "classpath:/six/")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContextConsumer<AssertableApplicationContext> assertResourceProperties(Consumer<Resources> consumer) {
|
||||||
|
return (context) -> {
|
||||||
|
assertThat(context).hasSingleBean(WebProperties.class);
|
||||||
|
consumer.accept(context.getBean(WebProperties.class).getResources());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableConfigurationProperties(WebProperties.class)
|
||||||
|
static class TestConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.web;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources.Cache;
|
||||||
|
import org.springframework.http.CacheControl;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link WebProperties.Resources}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @author Kristine Jetzke
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
class WebPropertiesResourcesTests {
|
||||||
|
|
||||||
|
private final Resources properties = new WebProperties().getResources();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceChainNoCustomization() {
|
||||||
|
assertThat(this.properties.getChain().getEnabled()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceChainStrategyEnabled() {
|
||||||
|
this.properties.getChain().getStrategy().getFixed().setEnabled(true);
|
||||||
|
assertThat(this.properties.getChain().getEnabled()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceChainEnabled() {
|
||||||
|
this.properties.getChain().setEnabled(true);
|
||||||
|
assertThat(this.properties.getChain().getEnabled()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceChainDisabled() {
|
||||||
|
this.properties.getChain().setEnabled(false);
|
||||||
|
assertThat(this.properties.getChain().getEnabled()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultStaticLocationsAllEndWithTrailingSlash() {
|
||||||
|
assertThat(this.properties.getStaticLocations()).allMatch((location) -> location.endsWith("/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customStaticLocationsAreNormalizedToEndWithTrailingSlash() {
|
||||||
|
this.properties.setStaticLocations(new String[] { "/foo", "/bar", "/baz/" });
|
||||||
|
String[] actual = this.properties.getStaticLocations();
|
||||||
|
assertThat(actual).containsExactly("/foo/", "/bar/", "/baz/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyCacheControl() {
|
||||||
|
CacheControl cacheControl = this.properties.getCache().getCachecontrol().toHttpCacheControl();
|
||||||
|
assertThat(cacheControl).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cacheControlAllPropertiesSet() {
|
||||||
|
Cache.Cachecontrol properties = this.properties.getCache().getCachecontrol();
|
||||||
|
properties.setMaxAge(Duration.ofSeconds(4));
|
||||||
|
properties.setCachePrivate(true);
|
||||||
|
properties.setCachePublic(true);
|
||||||
|
properties.setMustRevalidate(true);
|
||||||
|
properties.setNoTransform(true);
|
||||||
|
properties.setProxyRevalidate(true);
|
||||||
|
properties.setSMaxAge(Duration.ofSeconds(5));
|
||||||
|
properties.setStaleIfError(Duration.ofSeconds(6));
|
||||||
|
properties.setStaleWhileRevalidate(Duration.ofSeconds(7));
|
||||||
|
CacheControl cacheControl = properties.toHttpCacheControl();
|
||||||
|
assertThat(cacheControl.getHeaderValue())
|
||||||
|
.isEqualTo("max-age=4, must-revalidate, no-transform, public, private, proxy-revalidate,"
|
||||||
|
+ " s-maxage=5, stale-if-error=6, stale-while-revalidate=7");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidCacheControlCombination() {
|
||||||
|
Cache.Cachecontrol properties = this.properties.getCache().getCachecontrol();
|
||||||
|
properties.setMaxAge(Duration.ofSeconds(4));
|
||||||
|
properties.setNoStore(true);
|
||||||
|
CacheControl cacheControl = properties.toHttpCacheControl();
|
||||||
|
assertThat(cacheControl.getHeaderValue()).isEqualTo("no-store");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,8 @@ import javax.validation.ValidatorFactory;
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions;
|
import org.assertj.core.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
||||||
|
|
@ -167,16 +169,18 @@ class WebFluxAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void shouldNotMapResourcesWhenDisabled() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.add-mappings:false")
|
void shouldNotMapResourcesWhenDisabled(String prefix) {
|
||||||
|
this.contextRunner.withPropertyValues(prefix + ".add-mappings:false")
|
||||||
.run((context) -> assertThat(context.getBean("resourceHandlerMapping"))
|
.run((context) -> assertThat(context.getBean("resourceHandlerMapping"))
|
||||||
.isNotInstanceOf(SimpleUrlHandlerMapping.class));
|
.isNotInstanceOf(SimpleUrlHandlerMapping.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void resourceHandlerChainEnabled() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.chain.enabled:true").run((context) -> {
|
void resourceHandlerChainEnabled(String prefix) {
|
||||||
|
this.contextRunner.withPropertyValues(prefix + "chain.enabled:true").run((context) -> {
|
||||||
SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping", SimpleUrlHandlerMapping.class);
|
SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping", SimpleUrlHandlerMapping.class);
|
||||||
assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
|
assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
|
||||||
ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
|
ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
|
||||||
|
|
@ -406,10 +410,11 @@ class WebFluxAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void cachePeriod() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
|
void cachePeriod(String prefix) {
|
||||||
Assertions.setExtractBareNamePropertyMethods(false);
|
Assertions.setExtractBareNamePropertyMethods(false);
|
||||||
this.contextRunner.withPropertyValues("spring.resources.cache.period:5").run((context) -> {
|
this.contextRunner.withPropertyValues(prefix + "cache.period:5").run((context) -> {
|
||||||
Map<PathPattern, Object> handlerMap = getHandlerMap(context);
|
Map<PathPattern, Object> handlerMap = getHandlerMap(context);
|
||||||
assertThat(handlerMap).hasSize(2);
|
assertThat(handlerMap).hasSize(2);
|
||||||
for (Object handler : handlerMap.values()) {
|
for (Object handler : handlerMap.values()) {
|
||||||
|
|
@ -422,11 +427,12 @@ class WebFluxAutoConfigurationTests {
|
||||||
Assertions.setExtractBareNamePropertyMethods(true);
|
Assertions.setExtractBareNamePropertyMethods(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void cacheControl() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
|
void cacheControl(String prefix) {
|
||||||
Assertions.setExtractBareNamePropertyMethods(false);
|
Assertions.setExtractBareNamePropertyMethods(false);
|
||||||
this.contextRunner.withPropertyValues("spring.resources.cache.cachecontrol.max-age:5",
|
this.contextRunner.withPropertyValues(prefix + "cache.cachecontrol.max-age:5",
|
||||||
"spring.resources.cache.cachecontrol.proxy-revalidate:true").run((context) -> {
|
prefix + "cache.cachecontrol.proxy-revalidate:true").run((context) -> {
|
||||||
Map<PathPattern, Object> handlerMap = getHandlerMap(context);
|
Map<PathPattern, Object> handlerMap = getHandlerMap(context);
|
||||||
assertThat(handlerMap).hasSize(2);
|
assertThat(handlerMap).hasSize(2);
|
||||||
for (Object handler : handlerMap.values()) {
|
for (Object handler : handlerMap.values()) {
|
||||||
|
|
@ -449,14 +455,14 @@ class WebFluxAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void welcomePageHandlerMapping() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.static-locations=classpath:/welcome-page/")
|
void welcomePageHandlerMapping(String prefix) {
|
||||||
.run((context) -> {
|
this.contextRunner.withPropertyValues(prefix + "static-locations=classpath:/welcome-page/").run((context) -> {
|
||||||
assertThat(context).getBeans(RouterFunctionMapping.class).hasSize(2);
|
assertThat(context).getBeans(RouterFunctionMapping.class).hasSize(2);
|
||||||
assertThat(context.getBean("welcomePageRouterFunctionMapping", HandlerMapping.class)).isNotNull()
|
assertThat(context.getBean("welcomePageRouterFunctionMapping", HandlerMapping.class)).isNotNull()
|
||||||
.extracting("order").isEqualTo(1);
|
.extracting("order").isEqualTo(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
@ -62,7 +62,7 @@ class DefaultErrorWebExceptionHandlerTests {
|
||||||
@Test
|
@Test
|
||||||
void nonStandardErrorStatusCodeShouldNotFail() {
|
void nonStandardErrorStatusCodeShouldNotFail() {
|
||||||
ErrorAttributes errorAttributes = mock(ErrorAttributes.class);
|
ErrorAttributes errorAttributes = mock(ErrorAttributes.class);
|
||||||
ResourceProperties resourceProperties = new ResourceProperties();
|
Resources resourceProperties = new Resources();
|
||||||
ErrorProperties errorProperties = new ErrorProperties();
|
ErrorProperties errorProperties = new ErrorProperties();
|
||||||
ApplicationContext context = new AnnotationConfigReactiveWebApplicationContext();
|
ApplicationContext context = new AnnotationConfigReactiveWebApplicationContext();
|
||||||
given(errorAttributes.getErrorAttributes(any(), any())).willReturn(getErrorAttributes());
|
given(errorAttributes.getErrorAttributes(any(), any())).willReturn(getErrorAttributes());
|
||||||
|
|
|
||||||
|
|
@ -204,15 +204,17 @@ class WebMvcAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void resourceHandlerMappingDisabled() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.add-mappings:false")
|
void resourceHandlerMappingDisabled(String prefix) {
|
||||||
|
this.contextRunner.withPropertyValues(prefix + "add-mappings:false")
|
||||||
.run((context) -> assertThat(getResourceMappingLocations(context)).hasSize(0));
|
.run((context) -> assertThat(getResourceMappingLocations(context)).hasSize(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void resourceHandlerChainEnabled() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.chain.enabled:true").run((context) -> {
|
void resourceHandlerChainEnabled(String prefix) {
|
||||||
|
this.contextRunner.withPropertyValues(prefix + "chain.enabled:true").run((context) -> {
|
||||||
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(2);
|
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(2);
|
||||||
assertThat(getResourceTransformers(context, "/webjars/**")).hasSize(1);
|
assertThat(getResourceTransformers(context, "/webjars/**")).hasSize(1);
|
||||||
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass")
|
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass")
|
||||||
|
|
@ -222,11 +224,13 @@ class WebMvcAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void resourceHandlerFixedStrategyEnabled() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.chain.strategy.fixed.enabled:true",
|
void resourceHandlerFixedStrategyEnabled(String prefix) {
|
||||||
"spring.resources.chain.strategy.fixed.version:test",
|
this.contextRunner
|
||||||
"spring.resources.chain.strategy.fixed.paths:/**/*.js").run((context) -> {
|
.withPropertyValues(prefix + "chain.strategy.fixed.enabled:true",
|
||||||
|
prefix + "chain.strategy.fixed.version:test", prefix + "chain.strategy.fixed.paths:/**/*.js")
|
||||||
|
.run((context) -> {
|
||||||
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(3);
|
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(3);
|
||||||
assertThat(getResourceTransformers(context, "/webjars/**")).hasSize(2);
|
assertThat(getResourceTransformers(context, "/webjars/**")).hasSize(2);
|
||||||
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass").containsOnly(
|
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass").containsOnly(
|
||||||
|
|
@ -239,10 +243,11 @@ class WebMvcAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void resourceHandlerContentStrategyEnabled() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.chain.strategy.content.enabled:true",
|
void resourceHandlerContentStrategyEnabled(String prefix) {
|
||||||
"spring.resources.chain.strategy.content.paths:/**,/*.png").run((context) -> {
|
this.contextRunner.withPropertyValues(prefix + "chain.strategy.content.enabled:true",
|
||||||
|
prefix + "chain.strategy.content.paths:/**,/*.png").run((context) -> {
|
||||||
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(3);
|
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(3);
|
||||||
assertThat(getResourceTransformers(context, "/webjars/**")).hasSize(2);
|
assertThat(getResourceTransformers(context, "/webjars/**")).hasSize(2);
|
||||||
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass").containsOnly(
|
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass").containsOnly(
|
||||||
|
|
@ -255,25 +260,25 @@ class WebMvcAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
void resourceHandlerChainCustomized() {
|
void resourceHandlerChainCustomized(String prefix) {
|
||||||
this.contextRunner
|
this.contextRunner.withPropertyValues(prefix + "chain.enabled:true", prefix + "chain.cache:false",
|
||||||
.withPropertyValues("spring.resources.chain.enabled:true", "spring.resources.chain.cache:false",
|
prefix + "chain.strategy.content.enabled:true", prefix + "chain.strategy.content.paths:/**,/*.png",
|
||||||
"spring.resources.chain.strategy.content.enabled:true",
|
prefix + "chain.strategy.fixed.enabled:true", prefix + "chain.strategy.fixed.version:test",
|
||||||
"spring.resources.chain.strategy.content.paths:/**,/*.png",
|
prefix + "chain.strategy.fixed.paths:/**/*.js", prefix + "chain.html-application-cache:true",
|
||||||
"spring.resources.chain.strategy.fixed.enabled:true",
|
prefix + "chain.compressed:true").run((context) -> {
|
||||||
"spring.resources.chain.strategy.fixed.version:test",
|
|
||||||
"spring.resources.chain.strategy.fixed.paths:/**/*.js",
|
|
||||||
"spring.resources.chain.html-application-cache:true", "spring.resources.chain.compressed:true")
|
|
||||||
.run((context) -> {
|
|
||||||
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(3);
|
assertThat(getResourceResolvers(context, "/webjars/**")).hasSize(3);
|
||||||
assertThat(getResourceTransformers(context, "/webjars/**")).hasSize(2);
|
assertThat(getResourceTransformers(context, "/webjars/**"))
|
||||||
|
.hasSize(prefix.equals("spring.resources.") ? 2 : 1);
|
||||||
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass").containsOnly(
|
assertThat(getResourceResolvers(context, "/**")).extractingResultOf("getClass").containsOnly(
|
||||||
EncodedResourceResolver.class, VersionResourceResolver.class, PathResourceResolver.class);
|
EncodedResourceResolver.class, VersionResourceResolver.class, PathResourceResolver.class);
|
||||||
assertThat(getResourceTransformers(context, "/**")).extractingResultOf("getClass").containsOnly(
|
assertThat(getResourceTransformers(context, "/**")).extractingResultOf("getClass")
|
||||||
CssLinkResourceTransformer.class,
|
.containsOnly(prefix.equals("spring.resources.")
|
||||||
org.springframework.web.servlet.resource.AppCacheManifestTransformer.class);
|
? new Class<?>[] { CssLinkResourceTransformer.class,
|
||||||
|
org.springframework.web.servlet.resource.AppCacheManifestTransformer.class }
|
||||||
|
: new Class<?>[] { CssLinkResourceTransformer.class });
|
||||||
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers(context, "/**")
|
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers(context, "/**")
|
||||||
.get(1);
|
.get(1);
|
||||||
Map<String, VersionStrategy> strategyMap = resolver.getStrategyMap();
|
Map<String, VersionStrategy> strategyMap = resolver.getStrategyMap();
|
||||||
|
|
@ -597,19 +602,20 @@ class WebMvcAutoConfigurationTests {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void welcomePageHandlerMappingIsAutoConfigured() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.static-locations:classpath:/welcome-page/")
|
void welcomePageHandlerMappingIsAutoConfigured(String prefix) {
|
||||||
.run((context) -> {
|
this.contextRunner.withPropertyValues(prefix + "static-locations:classpath:/welcome-page/").run((context) -> {
|
||||||
assertThat(context).hasSingleBean(WelcomePageHandlerMapping.class);
|
assertThat(context).hasSingleBean(WelcomePageHandlerMapping.class);
|
||||||
WelcomePageHandlerMapping bean = context.getBean(WelcomePageHandlerMapping.class);
|
WelcomePageHandlerMapping bean = context.getBean(WelcomePageHandlerMapping.class);
|
||||||
assertThat(bean.getRootHandler()).isNotNull();
|
assertThat(bean.getRootHandler()).isNotNull();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void welcomePageHandlerIncludesCorsConfiguration() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.static-locations:classpath:/welcome-page/")
|
void welcomePageHandlerIncludesCorsConfiguration(String prefix) {
|
||||||
|
this.contextRunner.withPropertyValues(prefix + "static-locations:classpath:/welcome-page/")
|
||||||
.withUserConfiguration(CorsConfigurer.class).run((context) -> {
|
.withUserConfiguration(CorsConfigurer.class).run((context) -> {
|
||||||
WelcomePageHandlerMapping bean = context.getBean(WelcomePageHandlerMapping.class);
|
WelcomePageHandlerMapping bean = context.getBean(WelcomePageHandlerMapping.class);
|
||||||
UrlBasedCorsConfigurationSource source = (UrlBasedCorsConfigurationSource) ReflectionTestUtils
|
UrlBasedCorsConfigurationSource source = (UrlBasedCorsConfigurationSource) ReflectionTestUtils
|
||||||
|
|
@ -732,9 +738,10 @@ class WebMvcAutoConfigurationTests {
|
||||||
.run((context) -> assertThat(context).hasNotFailed());
|
.run((context) -> assertThat(context).hasNotFailed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void cachePeriod() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.cache.period:5").run(this::assertCachePeriod);
|
void cachePeriod(String prefix) {
|
||||||
|
this.contextRunner.withPropertyValues(prefix + "cache.period:5").run(this::assertCachePeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertCachePeriod(AssertableWebApplicationContext context) {
|
private void assertCachePeriod(AssertableWebApplicationContext context) {
|
||||||
|
|
@ -749,10 +756,11 @@ class WebMvcAutoConfigurationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void cacheControl() {
|
@ValueSource(strings = { "spring.resources.", "spring.web.resources." })
|
||||||
this.contextRunner.withPropertyValues("spring.resources.cache.cachecontrol.max-age:5",
|
void cacheControl(String prefix) {
|
||||||
"spring.resources.cache.cachecontrol.proxy-revalidate:true").run(this::assertCacheControl);
|
this.contextRunner.withPropertyValues(prefix + "cache.cachecontrol.max-age:5",
|
||||||
|
prefix + "cache.cachecontrol.proxy-revalidate:true").run(this::assertCacheControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider;
|
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider;
|
||||||
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
|
@ -65,7 +65,7 @@ class DefaultErrorViewResolverTests {
|
||||||
@Mock
|
@Mock
|
||||||
private TemplateAvailabilityProvider templateAvailabilityProvider;
|
private TemplateAvailabilityProvider templateAvailabilityProvider;
|
||||||
|
|
||||||
private ResourceProperties resourceProperties;
|
private Resources resourcesProperties;
|
||||||
|
|
||||||
private Map<String, Object> model = new HashMap<>();
|
private Map<String, Object> model = new HashMap<>();
|
||||||
|
|
||||||
|
|
@ -75,25 +75,24 @@ class DefaultErrorViewResolverTests {
|
||||||
void setup() {
|
void setup() {
|
||||||
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||||
applicationContext.refresh();
|
applicationContext.refresh();
|
||||||
this.resourceProperties = new ResourceProperties();
|
this.resourcesProperties = new Resources();
|
||||||
TemplateAvailabilityProviders templateAvailabilityProviders = new TestTemplateAvailabilityProviders(
|
TemplateAvailabilityProviders templateAvailabilityProviders = new TestTemplateAvailabilityProviders(
|
||||||
this.templateAvailabilityProvider);
|
this.templateAvailabilityProvider);
|
||||||
this.resolver = new DefaultErrorViewResolver(applicationContext, this.resourceProperties,
|
this.resolver = new DefaultErrorViewResolver(applicationContext, this.resourcesProperties,
|
||||||
templateAvailabilityProviders);
|
templateAvailabilityProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createWhenApplicationContextIsNullShouldThrowException() {
|
void createWhenApplicationContextIsNullShouldThrowException() {
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException().isThrownBy(() -> new DefaultErrorViewResolver(null, new Resources()))
|
||||||
.isThrownBy(() -> new DefaultErrorViewResolver(null, new ResourceProperties()))
|
|
||||||
.withMessageContaining("ApplicationContext must not be null");
|
.withMessageContaining("ApplicationContext must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createWhenResourcePropertiesIsNullShouldThrowException() {
|
void createWhenResourcePropertiesIsNullShouldThrowException() {
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> new DefaultErrorViewResolver(mock(ApplicationContext.class), null))
|
.isThrownBy(() -> new DefaultErrorViewResolver(mock(ApplicationContext.class), (Resources) null))
|
||||||
.withMessageContaining("ResourceProperties must not be null");
|
.withMessageContaining("Resources must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -197,7 +196,7 @@ class DefaultErrorViewResolverTests {
|
||||||
|
|
||||||
private void setResourceLocation(String path) {
|
private void setResourceLocation(String path) {
|
||||||
String packageName = getClass().getPackage().getName();
|
String packageName = getClass().getPackage().getName();
|
||||||
this.resourceProperties
|
this.resourcesProperties
|
||||||
.setStaticLocations(new String[] { "classpath:" + packageName.replace('.', '/') + path + "/" });
|
.setStaticLocations(new String[] { "classpath:" + packageName.replace('.', '/') + path + "/" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import java.util.Map;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.context.properties.bind.BindResult;
|
||||||
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
import org.springframework.boot.devtools.logger.DevToolsLogFactory;
|
import org.springframework.boot.devtools.logger.DevToolsLogFactory;
|
||||||
import org.springframework.boot.devtools.restart.Restarter;
|
import org.springframework.boot.devtools.restart.Restarter;
|
||||||
import org.springframework.boot.devtools.system.DevToolsEnablementDeducer;
|
import org.springframework.boot.devtools.system.DevToolsEnablementDeducer;
|
||||||
|
|
@ -85,7 +87,9 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro
|
||||||
if (canAddProperties(environment)) {
|
if (canAddProperties(environment)) {
|
||||||
logger.info(LogMessage.format("Devtools property defaults active! Set '%s' to 'false' to disable",
|
logger.info(LogMessage.format("Devtools property defaults active! Set '%s' to 'false' to disable",
|
||||||
ENABLED));
|
ENABLED));
|
||||||
environment.getPropertySources().addLast(new MapPropertySource("devtools", PROPERTIES));
|
Map<String, Object> properties = new HashMap<>(PROPERTIES);
|
||||||
|
properties.putAll(getResourceProperties(environment));
|
||||||
|
environment.getPropertySources().addLast(new MapPropertySource("devtools", properties));
|
||||||
}
|
}
|
||||||
if (isWebApplication(environment) && !environment.containsProperty(WEB_LOGGING)) {
|
if (isWebApplication(environment) && !environment.containsProperty(WEB_LOGGING)) {
|
||||||
logger.info(LogMessage.format(
|
logger.info(LogMessage.format(
|
||||||
|
|
@ -95,6 +99,28 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getResourceProperties(Environment environment) {
|
||||||
|
Map<String, String> resourceProperties = new HashMap<>();
|
||||||
|
String prefix = determineResourcePropertiesPrefix(environment);
|
||||||
|
resourceProperties.put(prefix + "cache.period", "0");
|
||||||
|
resourceProperties.put(prefix + "chain.cache", "false");
|
||||||
|
System.out.println(resourceProperties);
|
||||||
|
return resourceProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private String determineResourcePropertiesPrefix(Environment environment) {
|
||||||
|
if (ClassUtils.isPresent("org.springframework.boot.autoconfigure.web.ResourceProperties",
|
||||||
|
getClass().getClassLoader())) {
|
||||||
|
BindResult<org.springframework.boot.autoconfigure.web.ResourceProperties> result = Binder.get(environment)
|
||||||
|
.bind("spring.resources", org.springframework.boot.autoconfigure.web.ResourceProperties.class);
|
||||||
|
if (result.isBound() && result.get().hasBeenCustomized()) {
|
||||||
|
return "spring.resources.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "spring.web.resources.";
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isLocalApplication(ConfigurableEnvironment environment) {
|
private boolean isLocalApplication(ConfigurableEnvironment environment) {
|
||||||
return environment.getPropertySources().get("remoteUrl") == null;
|
return environment.getPropertySources().get("remoteUrl") == null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
|
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
|
||||||
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||||
import org.springframework.boot.devtools.classpath.ClassPathChangedEvent;
|
import org.springframework.boot.devtools.classpath.ClassPathChangedEvent;
|
||||||
import org.springframework.boot.devtools.classpath.ClassPathFileSystemWatcher;
|
import org.springframework.boot.devtools.classpath.ClassPathFileSystemWatcher;
|
||||||
|
|
@ -113,7 +114,17 @@ class LocalDevToolsAutoConfigurationTests {
|
||||||
@Test
|
@Test
|
||||||
void resourceCachePeriodIsZero() throws Exception {
|
void resourceCachePeriodIsZero() throws Exception {
|
||||||
this.context = getContext(() -> initializeAndRun(WebResourcesConfig.class));
|
this.context = getContext(() -> initializeAndRun(WebResourcesConfig.class));
|
||||||
ResourceProperties properties = this.context.getBean(ResourceProperties.class);
|
Resources properties = this.context.getBean(WebProperties.class).getResources();
|
||||||
|
assertThat(properties.getCache().getPeriod()).isZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
void deprecatedResourceCachePeriodIsZeroWhenDeprecatedResourcePropertiesAreInUse() throws Exception {
|
||||||
|
this.context = getContext(() -> initializeAndRun(WebResourcesConfig.class,
|
||||||
|
Collections.singletonMap("spring.resources.add-mappings", false)));
|
||||||
|
Resources properties = this.context
|
||||||
|
.getBean(org.springframework.boot.autoconfigure.web.ResourceProperties.class);
|
||||||
assertThat(properties.getCache().getPeriod()).isZero();
|
assertThat(properties.getCache().getPeriod()).isZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,9 +293,10 @@ class LocalDevToolsAutoConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Import({ ServletWebServerFactoryAutoConfiguration.class, LocalDevToolsAutoConfiguration.class,
|
@Import({ ServletWebServerFactoryAutoConfiguration.class, LocalDevToolsAutoConfiguration.class, WebProperties.class,
|
||||||
ResourceProperties.class })
|
org.springframework.boot.autoconfigure.web.ResourceProperties.class })
|
||||||
static class WebResourcesConfig {
|
static class WebResourcesConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2608,7 +2608,7 @@ For instance, relocating all resources to `/resources/**` can be achieved as fol
|
||||||
static-path-pattern: "/resources/**"
|
static-path-pattern: "/resources/**"
|
||||||
----
|
----
|
||||||
|
|
||||||
You can also customize the static resource locations by using the configprop:spring.resources.static-locations[] property (replacing the default values with a list of directory locations).
|
You can also customize the static resource locations by using the configprop:spring.web.resources.static-locations[] property (replacing the default values with a list of directory locations).
|
||||||
The root Servlet context path, `"/"`, is automatically added as a location as well.
|
The root Servlet context path, `"/"`, is automatically added as a location as well.
|
||||||
|
|
||||||
In addition to the "`standard`" static resource locations mentioned earlier, a special case is made for https://www.webjars.org/[Webjars content].
|
In addition to the "`standard`" static resource locations mentioned earlier, a special case is made for https://www.webjars.org/[Webjars content].
|
||||||
|
|
@ -2631,12 +2631,13 @@ To use cache busting, the following configuration configures a cache busting sol
|
||||||
[source,yaml,indent=0,subs="verbatim,quotes,attributes",configprops,configblocks]
|
[source,yaml,indent=0,subs="verbatim,quotes,attributes",configprops,configblocks]
|
||||||
----
|
----
|
||||||
spring:
|
spring:
|
||||||
resources:
|
web:
|
||||||
chain:
|
resources:
|
||||||
strategy:
|
chain:
|
||||||
content:
|
strategy:
|
||||||
enabled: true
|
content:
|
||||||
paths: "/**"
|
enabled: true
|
||||||
|
paths: "/**"
|
||||||
----
|
----
|
||||||
|
|
||||||
NOTE: Links to resources are rewritten in templates at runtime, thanks to a `ResourceUrlEncodingFilter` that is auto-configured for Thymeleaf and FreeMarker.
|
NOTE: Links to resources are rewritten in templates at runtime, thanks to a `ResourceUrlEncodingFilter` that is auto-configured for Thymeleaf and FreeMarker.
|
||||||
|
|
@ -2650,16 +2651,17 @@ A "fixed" strategy adds a static version string in the URL without changing the
|
||||||
[source,yaml,indent=0,subs="verbatim,quotes,attributes",configprops,configblocks]
|
[source,yaml,indent=0,subs="verbatim,quotes,attributes",configprops,configblocks]
|
||||||
----
|
----
|
||||||
spring:
|
spring:
|
||||||
resources:
|
web:
|
||||||
chain:
|
resources:
|
||||||
strategy:
|
chain:
|
||||||
content:
|
strategy:
|
||||||
enabled: true
|
content:
|
||||||
paths: "/**"
|
enabled: true
|
||||||
fixed:
|
paths: "/**"
|
||||||
enabled: true
|
fixed:
|
||||||
paths: "/js/lib/"
|
enabled: true
|
||||||
version: "v12"
|
paths: "/js/lib/"
|
||||||
|
version: "v12"
|
||||||
----
|
----
|
||||||
|
|
||||||
With this configuration, JavaScript modules located under `"/js/lib/"` use a fixed versioning strategy (`"/v12/js/lib/mymodule.js"`), while other resources still use the content one (`<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>`).
|
With this configuration, JavaScript modules located under `"/js/lib/"` use a fixed versioning strategy (`"/v12/js/lib/mymodule.js"`), while other resources still use the content one (`<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>`).
|
||||||
|
|
@ -3134,7 +3136,7 @@ For instance, relocating all resources to `/resources/**` can be achieved as fol
|
||||||
static-path-pattern: "/resources/**"
|
static-path-pattern: "/resources/**"
|
||||||
----
|
----
|
||||||
|
|
||||||
You can also customize the static resource locations by using `spring.resources.static-locations`.
|
You can also customize the static resource locations by using `spring.web.resources.static-locations`.
|
||||||
Doing so replaces the default values with a list of directory locations.
|
Doing so replaces the default values with a list of directory locations.
|
||||||
If you do so, the default welcome page detection switches to your custom locations.
|
If you do so, the default welcome page detection switches to your custom locations.
|
||||||
So, if there is an `index.html` in any of your locations on startup, it is the home page of the application.
|
So, if there is an `index.html` in any of your locations on startup, it is the home page of the application.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue