Move server.error properties to spring.web.error

Closes gh-48201
This commit is contained in:
Andy Wilkinson
2025-11-20 11:55:23 +00:00
parent 2b306329ae
commit 3becdc7d47
37 changed files with 161 additions and 125 deletions
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.http.CacheControl;
@@ -50,6 +51,9 @@ public class WebProperties {
private final Resources resources = new Resources();
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
public @Nullable Locale getLocale() {
return this.locale;
}
@@ -66,6 +70,10 @@ public class WebProperties {
this.localeResolver = localeResolver;
}
public ErrorProperties getError() {
return this.error;
}
public Resources getResources() {
return this.resources;
}
@@ -1,3 +1,6 @@
defaults.spring.template.provider.cache=false
defaults.spring.web.error.include-binding-errors=always
defaults.spring.web.error.include-message=always
defaults.spring.web.error.include-stacktrace=always
defaults.spring.web.resources.cache.period=0
defaults.spring.web.resources.chain.cache=false
@@ -247,7 +247,7 @@ For more detail, see the following sections:
Spring Boot installs a '`whitelabel`' error page that you see in a browser client if you encounter a server error (machine clients consuming JSON and other media types should see a sensible response with the right error code).
NOTE: Set `server.error.whitelabel.enabled=false` to switch the default error page off.
NOTE: Set configprop:spring.web.error.whitelabel.enabled[] to `false` to switch the default error page off.
Doing so restores the default of the servlet container that you are using.
Note that Spring Boot still tries to resolve the error view, so you should probably add your own error page rather than disabling it completely.
@@ -325,7 +325,7 @@ Usually, you would define the properties in your `application.properties` or `ap
Common server settings include:
* Network settings: Listen port for incoming HTTP requests (`server.port`), interface address to bind to (`server.address`), and so on.
* Error management: Location of the error page (`server.error.path`) and so on.
* Error management: Location of the error page (configprop:spring.web.error.path[]) and so on.
* xref:how-to:webserver.adoc#howto.webserver.configure-ssl[SSL]
* xref:how-to:webserver.adoc#howto.webserver.enable-response-compression[HTTP compression]
@@ -636,7 +636,7 @@ Common server settings include:
* Network settings: Listen port for incoming HTTP requests (`server.port`), interface address to bind to (`server.address`), and so on.
* Session settings: Whether the session is persistent (`server.servlet.session.persistent`), session timeout (`server.servlet.session.timeout`), location of session data (`server.servlet.session.store-dir`), and session-cookie configuration (`server.servlet.session.cookie.*`).
* Error management: Location of the error page (`server.error.path`) and so on.
* Error management: Location of the error page (configprop:spring.web.error.path[]) and so on.
* xref:how-to:webserver.adoc#howto.webserver.configure-ssl[SSL]
* xref:how-to:webserver.adoc#howto.webserver.enable-response-compression[HTTP compression]
@@ -706,7 +706,7 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
contextCustomizer.accept(applicationContext);
Map<String, Object> properties = new HashMap<>();
properties.put("endpointPath", endpointPath);
properties.put("server.error.include-message", "always");
properties.put("spring.web.error.include-message", "always");
applicationContext.getEnvironment().getPropertySources().addLast(new MapPropertySource("test", properties));
applicationContext.refresh();
try {
@@ -286,8 +286,7 @@ class LocalDevToolsAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@Import({ TomcatServletWebServerAutoConfiguration.class, LocalDevToolsAutoConfiguration.class,
WebProperties.class })
@Import({ TomcatServletWebServerAutoConfiguration.class, LocalDevToolsAutoConfiguration.class })
static class WebResourcesConfig {
}
@@ -21,6 +21,8 @@ import org.apache.tomcat.websocket.server.WsSci;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWarDeployment;
import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.thread.Threading;
import org.springframework.boot.tomcat.autoconfigure.reactive.TomcatReactiveWebServerAutoConfiguration;
import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration;
@@ -39,12 +41,13 @@ import org.springframework.core.env.Environment;
*/
@ConditionalOnNotWarDeployment
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public class TomcatWebServerConfiguration {
@Bean
TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties, TomcatServerProperties tomcatProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties, tomcatProperties);
ServerProperties serverProperties, TomcatServerProperties tomcatProperties, WebProperties webProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties, tomcatProperties, webProperties);
}
@Bean
@@ -34,6 +34,7 @@ import org.apache.coyote.http2.Http2Protocol;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeAttribute;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.tomcat.ConfigurableTomcatWebServerFactory;
@@ -77,11 +78,14 @@ public class TomcatWebServerFactoryCustomizer
private final TomcatServerProperties tomcatProperties;
private final WebProperties webProperties;
public TomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties,
TomcatServerProperties tomcatProperties) {
TomcatServerProperties tomcatProperties, WebProperties webProperties) {
this.environment = environment;
this.serverProperties = serverProperties;
this.tomcatProperties = tomcatProperties;
this.webProperties = webProperties;
}
@Override
@@ -161,7 +165,7 @@ public class TomcatWebServerFactoryCustomizer
.as((enable) -> !enable)
.to(factory::setDisableMBeanRegistry);
customizeStaticResources(factory);
customizeErrorReportValve(this.serverProperties.getError(), factory);
customizeErrorReportValve(this.webProperties.getError(), factory);
factory.setUseApr(getUseApr(this.tomcatProperties.getUseApr()));
}
@@ -34,6 +34,7 @@ import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
@@ -74,13 +75,15 @@ class TomcatWebServerFactoryCustomizerTests {
private final TomcatServerProperties tomcatProperties = new TomcatServerProperties();
private final WebProperties webProperties = new WebProperties();
private TomcatWebServerFactoryCustomizer customizer;
@BeforeEach
void setup() {
ConfigurationPropertySources.attach(this.environment);
this.customizer = new TomcatWebServerFactoryCustomizer(this.environment, this.serverProperties,
this.tomcatProperties);
this.tomcatProperties, this.webProperties);
}
@Test
@@ -26,7 +26,6 @@ import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.convert.DurationUnit;
@@ -83,9 +82,6 @@ public class ServerProperties {
*/
private @Nullable InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
/**
* Strategy for handling X-Forwarded-* headers.
*/
@@ -168,10 +164,6 @@ public class ServerProperties {
this.shutdown = shutdown;
}
public ErrorProperties getError() {
return this.error;
}
public @Nullable Ssl getSsl() {
return this.ssl;
}
@@ -12,37 +12,72 @@
{
"name": "server.error.include-binding-errors",
"description": "When to include \"errors\" attribute.",
"defaultValue": "never"
"defaultValue": "never",
"deprecation": {
"level": "error",
"replacement": "spring.web.error.include-binding-errors",
"since": "4.0.0"
}
},
{
"name": "server.error.include-exception",
"description": "Include the \"exception\" attribute.",
"defaultValue": false
"defaultValue": false,
"deprecation": {
"level": "error",
"replacement": "spring.web.error.include-exception",
"since": "4.0.0"
}
},
{
"name": "server.error.include-message",
"description": "When to include the \"message\" attribute.",
"defaultValue": "never"
"defaultValue": "never",
"deprecation": {
"level": "error",
"replacement": "spring.web.error.include-message",
"since": "4.0.0"
}
},
{
"name": "server.error.include-path",
"description": "When to include the \"path\" attribute.",
"defaultValue": "always"
"defaultValue": "always",
"deprecation": {
"level": "error",
"replacement": "spring.web.error.include-path",
"since": "4.0.0"
}
},
{
"name": "server.error.include-stacktrace",
"description": "When to include the \"trace\" attribute.",
"defaultValue": "never"
"defaultValue": "never",
"deprecation": {
"level": "error",
"replacement": "spring.web.error.include-stacktrace",
"since": "4.0.0"
}
},
{
"name": "server.error.path",
"description": "Path of the error controller",
"defaultValue": "/error"
"defaultValue": "/error",
"deprecation": {
"level": "error",
"replacement": "spring.web.error.path",
"since": "4.0.0"
}
},
{
"name": "server.error.whitelabel.enabled",
"description": "Whether to enable the default error page displayed in browsers in case of a server error.",
"defaultValue": true
"defaultValue": true ,
"deprecation": {
"level": "error",
"replacement": "spring.web.error.whitelabel.enabled",
"since": "4.0.0"
}
},
{
"name": "server.max-http-header-size",
@@ -1,5 +1,2 @@
defaults.server.error.include-binding-errors=always
defaults.server.error.include-message=always
defaults.server.error.include-stacktrace=always
defaults.server.servlet.jsp.init-parameters.development=true
defaults.server.servlet.session.persistent=true
@@ -26,7 +26,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.webflux.autoconfigure.WebFluxAutoConfiguration;
import org.springframework.boot.webflux.error.DefaultErrorAttributes;
import org.springframework.boot.webflux.error.ErrorAttributes;
@@ -49,15 +48,9 @@ import org.springframework.web.reactive.result.view.ViewResolver;
@AutoConfiguration(before = WebFluxAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@EnableConfigurationProperties({ ServerProperties.class, WebProperties.class })
@EnableConfigurationProperties(WebProperties.class)
public final class ErrorWebFluxAutoConfiguration {
private final ServerProperties serverProperties;
ErrorWebFluxAutoConfiguration(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
@Order(-1)
@@ -65,7 +58,7 @@ public final class ErrorWebFluxAutoConfiguration {
ObjectProvider<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer,
ApplicationContext applicationContext) {
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
webProperties.getResources(), this.serverProperties.getError(), applicationContext);
webProperties.getResources(), webProperties.getError(), applicationContext);
exceptionHandler.setViewResolvers(viewResolvers.orderedStream().toList());
exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
@@ -41,7 +41,6 @@ import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.webflux.autoconfigure.HttpHandlerAutoConfiguration;
import org.springframework.boot.webflux.autoconfigure.WebFluxAutoConfiguration;
import org.springframework.boot.webflux.error.DefaultErrorAttributes;
@@ -181,7 +180,8 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void bindingResultErrorIncludeMessageAndErrors() {
this.contextRunner
.withPropertyValues("server.error.include-message=on-param", "server.error.include-binding-errors=on-param")
.withPropertyValues("spring.web.error.include-message=on-param",
"spring.web.error.include-binding-errors=on-param")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.post()
@@ -212,7 +212,8 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void includeStackTraceOnParam() {
this.contextRunner
.withPropertyValues("server.error.include-exception=true", "server.error.include-stacktrace=on-param")
.withPropertyValues("spring.web.error.include-exception=true",
"spring.web.error.include-stacktrace=on-param")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
@@ -237,7 +238,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void alwaysIncludeStackTrace() {
this.contextRunner
.withPropertyValues("server.error.include-exception=true", "server.error.include-stacktrace=always")
.withPropertyValues("spring.web.error.include-exception=true", "spring.web.error.include-stacktrace=always")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
@@ -262,7 +263,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void neverIncludeStackTrace() {
this.contextRunner
.withPropertyValues("server.error.include-exception=true", "server.error.include-stacktrace=never")
.withPropertyValues("spring.web.error.include-exception=true", "spring.web.error.include-stacktrace=never")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
@@ -287,7 +288,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void includeMessageOnParam() {
this.contextRunner
.withPropertyValues("server.error.include-exception=true", "server.error.include-message=on-param")
.withPropertyValues("spring.web.error.include-exception=true", "spring.web.error.include-message=on-param")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
@@ -312,7 +313,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void alwaysIncludeMessage() {
this.contextRunner
.withPropertyValues("server.error.include-exception=true", "server.error.include-message=always")
.withPropertyValues("spring.web.error.include-exception=true", "spring.web.error.include-message=always")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
@@ -337,7 +338,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void neverIncludeMessage() {
this.contextRunner
.withPropertyValues("server.error.include-exception=true", "server.error.include-message=never")
.withPropertyValues("spring.web.error.include-exception=true", "spring.web.error.include-message=never")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
@@ -361,7 +362,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void statusException() {
this.contextRunner.withPropertyValues("server.error.include-exception=true").run((context) -> {
this.contextRunner.withPropertyValues("spring.web.error.include-exception=true").run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
.uri("/badRequest")
@@ -383,8 +384,8 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void defaultErrorView() {
this.contextRunner
.withPropertyValues("spring.mustache.prefix=classpath:/unknown/", "server.error.include-stacktrace=always",
"server.error.include-message=always")
.withPropertyValues("spring.mustache.prefix=classpath:/unknown/",
"spring.web.error.include-stacktrace=always", "spring.web.error.include-message=always")
.run((context) -> {
WebTestClient client = getWebClient(context);
String body = client.get()
@@ -408,7 +409,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void escapeHtmlInDefaultErrorView() {
this.contextRunner
.withPropertyValues("spring.mustache.prefix=classpath:/unknown/", "server.error.include-message=always")
.withPropertyValues("spring.mustache.prefix=classpath:/unknown/", "spring.web.error.include-message=always")
.run((context) -> {
WebTestClient client = getWebClient(context);
String body = client.get()
@@ -464,7 +465,8 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Test
void whitelabelDisabled() {
this.contextRunner
.withPropertyValues("server.error.whitelabel.enabled=false", "spring.mustache.prefix=classpath:/unknown/")
.withPropertyValues("spring.web.error.whitelabel.enabled=false",
"spring.mustache.prefix=classpath:/unknown/")
.run((context) -> {
WebTestClient client = getWebClient(context);
client.get()
@@ -667,12 +669,11 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
@Bean
@Order(-1)
ErrorWebExceptionHandler errorWebExceptionHandler(ServerProperties serverProperties,
ErrorAttributes errorAttributes, WebProperties webProperties,
ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties webProperties,
ObjectProvider<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer,
ApplicationContext applicationContext) {
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
webProperties.getResources(), serverProperties.getError(), applicationContext) {
webProperties.getResources(), webProperties.getError(), applicationContext) {
@Override
protected ErrorAttributeOptions getErrorAttributeOptions(ServerRequest request, MediaType mediaType) {
@@ -21,7 +21,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletPath;
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.boot.webmvc.autoconfigure.WebMvcProperties;
@@ -42,7 +41,7 @@ import org.springframework.web.servlet.DispatcherServlet;
*/
@AutoConfiguration(after = WebMvcAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties({ ServerProperties.class, WebMvcProperties.class })
@EnableConfigurationProperties(WebMvcProperties.class)
@Import({ MockMvcConfiguration.class, MockMvcTesterConfiguration.class })
public final class MockMvcAutoConfiguration {
@@ -55,7 +55,7 @@ public class ManagementErrorEndpoint {
this.errorProperties = errorProperties;
}
@RequestMapping("${server.error.path:${error.path:/error}}")
@RequestMapping("${spring.web.error.path:${error.path:/error}}")
@ResponseBody
public Map<String, @Nullable Object> invoke(ServletWebRequest request) {
return this.errorAttributes.getErrorAttributes(request, getErrorAttributeOptions(request));
@@ -24,10 +24,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.servlet.filter.OrderedRequestContextFilter;
import org.springframework.boot.web.error.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory;
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration;
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletRegistrationBean;
@@ -61,14 +61,14 @@ class WebMvcEndpointChildContextConfiguration {
*/
@Bean
@ConditionalOnBean(ErrorAttributes.class)
ManagementErrorEndpoint errorEndpoint(ErrorAttributes errorAttributes, ServerProperties serverProperties) {
return new ManagementErrorEndpoint(errorAttributes, serverProperties.getError());
ManagementErrorEndpoint errorEndpoint(ErrorAttributes errorAttributes, WebProperties webProperties) {
return new ManagementErrorEndpoint(errorAttributes, webProperties.getError());
}
@Bean
@ConditionalOnBean(ErrorAttributes.class)
ManagementErrorPageCustomizer managementErrorPageCustomizer(ServerProperties serverProperties) {
return new ManagementErrorPageCustomizer(serverProperties);
ManagementErrorPageCustomizer managementErrorPageCustomizer(WebProperties webProperties) {
return new ManagementErrorPageCustomizer(webProperties);
}
@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
@@ -115,9 +115,9 @@ class WebMvcEndpointChildContextConfiguration {
static class ManagementErrorPageCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final ServerProperties properties;
private final WebProperties properties;
ManagementErrorPageCustomizer(ServerProperties properties) {
ManagementErrorPageCustomizer(WebProperties properties) {
this.properties = properties;
}
@@ -57,7 +57,7 @@ import org.springframework.web.servlet.ModelAndView;
* @see ErrorProperties
*/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
@RequestMapping("${spring.web.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
@@ -51,7 +51,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.error.ErrorPage;
import org.springframework.boot.web.error.ErrorPageRegistrar;
import org.springframework.boot.web.error.ErrorPageRegistry;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletPath;
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.boot.webmvc.autoconfigure.WebMvcProperties;
@@ -87,14 +86,14 @@ import org.springframework.web.util.HtmlUtils;
// Load before the main WebMvcAutoConfiguration so that the error View is available
@AutoConfiguration(before = WebMvcAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, ServerProperties.class })
@EnableConfigurationProperties({ ServerProperties.class, WebMvcProperties.class })
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@EnableConfigurationProperties({ WebProperties.class, WebMvcProperties.class })
public final class ErrorMvcAutoConfiguration {
private final ServerProperties serverProperties;
private final WebProperties webProperties;
ErrorMvcAutoConfiguration(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
ErrorMvcAutoConfiguration(WebProperties webProperties) {
this.webProperties = webProperties;
}
@Bean
@@ -107,13 +106,13 @@ public final class ErrorMvcAutoConfiguration {
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
return new BasicErrorController(errorAttributes, this.webProperties.getError(),
errorViewResolvers.orderedStream().toList());
}
@Bean
ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
return new ErrorPageCustomizer(this.webProperties, dispatcherServletPath);
}
@Bean
@@ -122,7 +121,6 @@ public final class ErrorMvcAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ WebProperties.class, WebMvcProperties.class })
static class DefaultErrorViewResolverConfiguration {
private final ApplicationContext applicationContext;
@@ -144,7 +142,7 @@ public final class ErrorMvcAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBooleanProperty(name = "server.error.whitelabel.enabled", matchIfMissing = true)
@ConditionalOnBooleanProperty(name = "spring.web.error.whitelabel.enabled", matchIfMissing = true)
@Conditional(ErrorTemplateMissingCondition.class)
protected static class WhitelabelErrorViewConfiguration {
@@ -263,11 +261,11 @@ public final class ErrorMvcAutoConfiguration {
*/
static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
private final ServerProperties properties;
private final WebProperties properties;
private final DispatcherServletPath dispatcherServletPath;
protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {
protected ErrorPageCustomizer(WebProperties properties, DispatcherServletPath dispatcherServletPath) {
this.properties = properties;
this.dispatcherServletPath = dispatcherServletPath;
}
@@ -79,8 +79,8 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
.withUserConfiguration(SucceedingEndpoint.class, FailingEndpoint.class, FailingControllerEndpoint.class)
.withInitializer(new ServerPortInfoApplicationContextInitializer())
.withPropertyValues("server.port=0", "management.server.port=0", "management.endpoints.web.exposure.include=*",
"server.error.include-exception=true", "server.error.include-message=always",
"server.error.include-binding-errors=always");
"spring.web.error.include-exception=true", "spring.web.error.include-message=always",
"spring.web.error.include-binding-errors=always");
@TempDir
@SuppressWarnings("NullAway.Init")
@@ -102,7 +102,8 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
@Test
void errorPageAndErrorControllerIncludeDetails() {
this.runner.withPropertyValues("server.error.include-stacktrace=always", "server.error.include-message=always")
this.runner
.withPropertyValues("spring.web.error.include-stacktrace=always", "spring.web.error.include-message=always")
.run(withRestClient((client) -> {
Map<String, ?> body = client.get()
.uri("actuator/fail")
@@ -95,7 +95,7 @@ class BasicErrorControllerDirectMockMvcTests {
@Test
void errorPageNotAvailableWithWhitelabelDisabled() {
setup((ConfigurableWebApplicationContext) new SpringApplication(WebMvcIncludedConfiguration.class)
.run("--server.port=0", "--server.error.whitelabel.enabled=false"));
.run("--server.port=0", "--spring.web.error.whitelabel.enabled=false"));
assertThat(this.mvc.get().uri("/error").accept(MediaType.TEXT_HTML)).hasFailed()
.failure()
.isInstanceOf(ServletException.class);
@@ -41,12 +41,12 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.http.converter.autoconfigure.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.resttestclient.TestRestTemplate;
import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration;
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.boot.webmvc.error.ErrorAttributes;
@@ -104,43 +104,43 @@ class BasicErrorControllerIntegrationTests {
@Test
void testErrorForMachineClientWithParamsTrue() {
load("--server.error.include-exception=true", "--server.error.include-stacktrace=on-param",
"--server.error.include-message=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-stacktrace=on-param",
"--spring.web.error.include-message=on-param");
exceptionWithStackTraceAndMessage("?trace=true&message=true");
}
@Test
void testErrorForMachineClientWithParamsFalse() {
load("--server.error.include-exception=true", "--server.error.include-stacktrace=on-param",
"--server.error.include-message=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-stacktrace=on-param",
"--spring.web.error.include-message=on-param");
exceptionWithoutStackTraceAndMessage("?trace=false&message=false");
}
@Test
void testErrorForMachineClientWithParamsAbsent() {
load("--server.error.include-exception=true", "--server.error.include-stacktrace=on-param",
"--server.error.include-message=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-stacktrace=on-param",
"--spring.web.error.include-message=on-param");
exceptionWithoutStackTraceAndMessage("");
}
@Test
void testErrorForMachineClientNeverParams() {
load("--server.error.include-exception=true", "--server.error.include-stacktrace=never",
"--server.error.include-message=never");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-stacktrace=never",
"--spring.web.error.include-message=never");
exceptionWithoutStackTraceAndMessage("?trace=true&message=true");
}
@Test
void testErrorForMachineClientAlwaysParams() {
load("--server.error.include-exception=true", "--server.error.include-stacktrace=always",
"--server.error.include-message=always");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-stacktrace=always",
"--spring.web.error.include-message=always");
exceptionWithStackTraceAndMessage("?trace=false&message=false");
}
@Test
@SuppressWarnings("rawtypes")
void testErrorForMachineClientAlwaysParamsWithoutMessage() {
load("--server.error.include-exception=true", "--server.error.include-message=always");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=always");
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/noMessage"), Map.class);
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", IllegalStateException.class,
"No message available", "/noMessage");
@@ -164,7 +164,7 @@ class BasicErrorControllerIntegrationTests {
@Test
@SuppressWarnings("rawtypes")
void testErrorForAnnotatedExceptionWithoutMessage() {
load("--server.error.include-exception=true");
load("--spring.web.error.include-exception=true");
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotated"), Map.class);
assertErrorAttributes(entity.getBody(), "400", "Bad Request", TestConfiguration.Errors.ExpectedException.class,
null, "/annotated");
@@ -173,7 +173,7 @@ class BasicErrorControllerIntegrationTests {
@Test
@SuppressWarnings("rawtypes")
void testErrorForAnnotatedExceptionWithMessage() {
load("--server.error.include-exception=true", "--server.error.include-message=always");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=always");
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotated"), Map.class);
assertErrorAttributes(entity.getBody(), "400", "Bad Request", TestConfiguration.Errors.ExpectedException.class,
"Expected!", "/annotated");
@@ -182,7 +182,7 @@ class BasicErrorControllerIntegrationTests {
@Test
@SuppressWarnings("rawtypes")
void testErrorForAnnotatedNoReasonExceptionWithoutMessage() {
load("--server.error.include-exception=true");
load("--spring.web.error.include-exception=true");
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotatedNoReason"), Map.class);
assertErrorAttributes(entity.getBody(), "406", "Not Acceptable",
TestConfiguration.Errors.NoReasonExpectedException.class, null, "/annotatedNoReason");
@@ -191,7 +191,7 @@ class BasicErrorControllerIntegrationTests {
@Test
@SuppressWarnings("rawtypes")
void testErrorForAnnotatedNoReasonExceptionWithMessage() {
load("--server.error.include-exception=true", "--server.error.include-message=always");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=always");
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotatedNoReason"), Map.class);
assertErrorAttributes(entity.getBody(), "406", "Not Acceptable",
TestConfiguration.Errors.NoReasonExpectedException.class, "Expected message", "/annotatedNoReason");
@@ -200,7 +200,7 @@ class BasicErrorControllerIntegrationTests {
@Test
@SuppressWarnings("rawtypes")
void testErrorForAnnotatedNoMessageExceptionWithMessage() {
load("--server.error.include-exception=true", "--server.error.include-message=always");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=always");
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotatedNoMessage"), Map.class);
assertErrorAttributes(entity.getBody(), "406", "Not Acceptable",
TestConfiguration.Errors.NoReasonExpectedException.class, "No message available",
@@ -209,61 +209,61 @@ class BasicErrorControllerIntegrationTests {
@Test
void testBindingExceptionForMachineClientWithErrorsParamTrue() {
load("--server.error.include-exception=true", "--server.error.include-binding-errors=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-binding-errors=on-param");
bindingExceptionWithErrors("?errors=true");
}
@Test
void testBindingExceptionForMachineClientWithErrorsParamFalse() {
load("--server.error.include-exception=true", "--server.error.include-binding-errors=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-binding-errors=on-param");
bindingExceptionWithoutErrors("?errors=false");
}
@Test
void testBindingExceptionForMachineClientWithErrorsParamAbsent() {
load("--server.error.include-exception=true", "--server.error.include-binding-errors=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-binding-errors=on-param");
bindingExceptionWithoutErrors("");
}
@Test
void testBindingExceptionForMachineClientAlwaysErrors() {
load("--server.error.include-exception=true", "--server.error.include-binding-errors=always");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-binding-errors=always");
bindingExceptionWithErrors("?errors=false");
}
@Test
void testBindingExceptionForMachineClientNeverErrors() {
load("--server.error.include-exception=true", "--server.error.include-binding-errors=never");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-binding-errors=never");
bindingExceptionWithoutErrors("?errors=true");
}
@Test
void testBindingExceptionForMachineClientWithMessageParamTrue() {
load("--server.error.include-exception=true", "--server.error.include-message=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=on-param");
bindingExceptionWithMessage("?message=true");
}
@Test
void testBindingExceptionForMachineClientWithMessageParamFalse() {
load("--server.error.include-exception=true", "--server.error.include-message=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=on-param");
bindingExceptionWithoutMessage("?message=false");
}
@Test
void testBindingExceptionForMachineClientWithMessageParamAbsent() {
load("--server.error.include-exception=true", "--server.error.include-message=on-param");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=on-param");
bindingExceptionWithoutMessage("");
}
@Test
void testBindingExceptionForMachineClientAlwaysMessage() {
load("--server.error.include-exception=true", "--server.error.include-message=always");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=always");
bindingExceptionWithMessage("?message=false");
}
@Test
void testBindingExceptionForMachineClientNeverMessage() {
load("--server.error.include-exception=true", "--server.error.include-message=never");
load("--spring.web.error.include-exception=true", "--spring.web.error.include-message=never");
bindingExceptionWithoutMessage("?message=true");
}
@@ -302,7 +302,7 @@ class BasicErrorControllerIntegrationTests {
@Test
@SuppressWarnings({ "rawtypes", "unchecked" })
void testRequestBodyValidationForMachineClient() {
load("--server.error.include-exception=true");
load("--spring.web.error.include-exception=true");
RequestEntity request = RequestEntity.post(URI.create(createUrl("/bodyValidation")))
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
@@ -509,9 +509,9 @@ class BasicErrorControllerIntegrationTests {
static class CustomErrorControllerWithoutStatusConfiguration extends TestConfiguration {
@Bean
BasicErrorController basicErrorController(ServerProperties serverProperties, ErrorAttributes errorAttributes,
BasicErrorController basicErrorController(WebProperties webProperties, ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
return new BasicErrorController(errorAttributes, serverProperties.getError(),
return new BasicErrorController(errorAttributes, webProperties.getError(),
errorViewResolvers.orderedStream().toList()) {
@Override
@@ -71,7 +71,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer
* @author Scott Frederick
*/
@SpringBootTest(properties = { "server.error.include-message=always", "debug=true" })
@SpringBootTest(properties = { "spring.web.error.include-message=always", "debug=true" })
@DirtiesContext
class BasicErrorControllerMockMvcTests {
@@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer
* @author Scott Frederick
*/
@SpringBootTest(properties = { "server.error.include-message=always" })
@SpringBootTest(properties = { "spring.web.error.include-message=always" })
@DirtiesContext
class DefaultErrorViewIntegrationTests {
@@ -37,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Scott Frederick
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "server.error.include-message=always" })
properties = { "spring.web.error.include-message=always" })
class SampleActuatorCustomSecurityApplicationTests extends AbstractSampleActuatorCustomSecurityTests {
@LocalServerPort
@@ -33,7 +33,7 @@ import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "server.error.include-message=always" })
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.web.error.include-message=always" })
@AutoConfigureTestRestTemplate
class SampleActuatorExtensionApplicationTests {
@@ -40,7 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Dave Syer
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "server.error.include-message=always" })
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.web.error.include-message=always" })
@AutoConfigureTestRestTemplate
class SampleActuatorUiApplicationTests {
@@ -1,3 +1,3 @@
server.error.path:/oops
spring.web.error.path:/oops
management.endpoint.health.show-details:always
management.endpoints.web.base-path:/admin
@@ -28,7 +28,7 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
classes = { AbstractErrorPageTests.TestConfiguration.class, ErrorPageTests.SecurityConfiguration.class,
SampleWebSecureApplication.class },
properties = { "server.error.include-message=always", "spring.security.user.name=username",
properties = { "spring.web.error.include-message=always", "spring.security.user.name=username",
"spring.security.user.password=password", "server.servlet.context-path=/example" })
class CustomContextPathErrorPageTests extends AbstractErrorPageTests {
@@ -26,7 +26,7 @@ import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { AbstractErrorPageTests.TestConfiguration.class,
UnauthenticatedErrorPageTests.SecurityConfiguration.class, SampleWebSecureApplication.class },
properties = { "server.error.include-message=always", "spring.security.user.name=username",
properties = { "spring.web.error.include-message=always", "spring.security.user.name=username",
"spring.security.user.password=password", "server.servlet.context-path=/example" })
class CustomContextPathUnauthenticatedErrorPageTests extends AbstractUnauthenticatedErrorPageTests {
@@ -33,7 +33,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
classes = { AbstractErrorPageTests.TestConfiguration.class,
CustomServletPathErrorPageTests.SecurityConfiguration.class, SampleWebSecureApplication.class },
properties = { "server.error.include-message=always", "spring.security.user.name=username",
properties = { "spring.web.error.include-message=always", "spring.security.user.name=username",
"spring.security.user.password=password", "spring.mvc.servlet.path=/custom/servlet/path" })
class CustomServletPathErrorPageTests extends AbstractErrorPageTests {
@@ -32,7 +32,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
classes = { AbstractErrorPageTests.TestConfiguration.class,
CustomServletPathUnauthenticatedErrorPageTests.SecurityConfiguration.class,
SampleWebSecureApplication.class },
properties = { "server.error.include-message=always", "spring.security.user.name=username",
properties = { "spring.web.error.include-message=always", "spring.security.user.name=username",
"spring.security.user.password=password", "spring.mvc.servlet.path=/custom/servlet/path" })
class CustomServletPathUnauthenticatedErrorPageTests extends AbstractUnauthenticatedErrorPageTests {
@@ -32,7 +32,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
classes = { AbstractErrorPageTests.TestConfiguration.class, ErrorPageTests.SecurityConfiguration.class,
SampleWebSecureApplication.class },
properties = { "server.error.include-message=always", "spring.security.user.name=username",
properties = { "spring.web.error.include-message=always", "spring.security.user.name=username",
"spring.security.user.password=password" })
class ErrorPageTests extends AbstractErrorPageTests {
@@ -33,7 +33,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
classes = { AbstractErrorPageTests.TestConfiguration.class, NoSessionErrorPageTests.SecurityConfiguration.class,
SampleWebSecureApplication.class },
properties = { "server.error.include-message=always", "spring.security.user.name=username",
properties = { "spring.web.error.include-message=always", "spring.security.user.name=username",
"spring.security.user.password=password" })
class NoSessionErrorPageTests extends AbstractErrorPageTests {
@@ -32,7 +32,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
classes = { AbstractErrorPageTests.TestConfiguration.class,
UnauthenticatedErrorPageTests.SecurityConfiguration.class, SampleWebSecureApplication.class },
properties = { "server.error.include-message=always", "spring.security.user.name=username",
properties = { "spring.web.error.include-message=always", "spring.security.user.name=username",
"spring.security.user.password=password" })
class UnauthenticatedErrorPageTests extends AbstractUnauthenticatedErrorPageTests {
@@ -37,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Brian Clozel
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "server.error.include-message=always")
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.web.error.include-message=always")
@AutoConfigureWebTestClient
class SampleWebFluxApplicationIntegrationTests {