Improve API of ErrorAttributes and DefaultErrorAttributes
This commit improves the backward-compatibility of the ErrorAttributes interfaces by providing a default implementation of a new method. It also encapsulates several parameters that control the inclusion or exclusion of error attributes into a new ErrorAttributeOptions type to make it easier and less intrusive to add additional options in the future. This encapsulation also makes the handling of the includeException option more similar to other options. Fixes gh-21324
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
@@ -53,10 +54,27 @@ public class ManagementErrorEndpoint {
|
|||||||
@RequestMapping("${server.error.path:${error.path:/error}}")
|
@RequestMapping("${server.error.path:${error.path:/error}}")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Map<String, Object> invoke(ServletWebRequest request) {
|
public Map<String, Object> invoke(ServletWebRequest request) {
|
||||||
return this.errorAttributes.getErrorAttributes(request, includeStackTrace(request), includeMessage(request),
|
return this.errorAttributes.getErrorAttributes(request, getErrorAttributeOptions(request));
|
||||||
includeBindingErrors(request));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ErrorAttributeOptions getErrorAttributeOptions(ServletWebRequest request) {
|
||||||
|
ErrorAttributeOptions options = ErrorAttributeOptions.defaults();
|
||||||
|
if (this.errorProperties.isIncludeException()) {
|
||||||
|
options = options.including(ErrorAttributeOptions.Include.EXCEPTION);
|
||||||
|
}
|
||||||
|
if (includeStackTrace(request)) {
|
||||||
|
options = options.including(ErrorAttributeOptions.Include.STACK_TRACE);
|
||||||
|
}
|
||||||
|
if (includeMessage(request)) {
|
||||||
|
options = options.including(ErrorAttributeOptions.Include.MESSAGE);
|
||||||
|
}
|
||||||
|
if (includeBindingErrors(request)) {
|
||||||
|
options = options.including(ErrorAttributeOptions.Include.BINDING_ERRORS);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
private boolean includeStackTrace(ServletWebRequest request) {
|
private boolean includeStackTrace(ServletWebRequest request) {
|
||||||
switch (this.errorProperties.getIncludeStacktrace()) {
|
switch (this.errorProperties.getIncludeStacktrace()) {
|
||||||
case ALWAYS:
|
case ALWAYS:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.boot.actuate.autoconfigure.web.servlet;
|
package org.springframework.boot.actuate.autoconfigure.web.servlet;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@@ -26,6 +27,7 @@ import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
|
|||||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.web.context.request.ServletWebRequest;
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@@ -103,4 +105,63 @@ class ManagementErrorEndpointTests {
|
|||||||
assertThat(response).doesNotContainKey("trace");
|
assertThat(response).doesNotContainKey("trace");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void errorResponseWithCustomErrorAttributesUsingDeprecatedApi() {
|
||||||
|
ErrorAttributes attributes = new ErrorAttributes() {
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("message", "An error occurred");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Throwable getError(WebRequest webRequest) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ManagementErrorEndpoint endpoint = new ManagementErrorEndpoint(attributes, this.errorProperties);
|
||||||
|
Map<String, Object> response = endpoint.invoke(new ServletWebRequest(new MockHttpServletRequest()));
|
||||||
|
assertThat(response).hasSize(1);
|
||||||
|
assertThat(response).containsEntry("message", "An error occurred");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void errorResponseWithDefaultErrorAttributesSubclassUsingDeprecatedApiAndDelegation() {
|
||||||
|
ErrorAttributes attributes = new DefaultErrorAttributes() {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||||
|
Map<String, Object> response = super.getErrorAttributes(webRequest, includeStackTrace);
|
||||||
|
response.put("error", "custom error");
|
||||||
|
response.put("custom", "value");
|
||||||
|
response.remove("path");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ManagementErrorEndpoint endpoint = new ManagementErrorEndpoint(attributes, this.errorProperties);
|
||||||
|
Map<String, Object> response = endpoint.invoke(new ServletWebRequest(new MockHttpServletRequest()));
|
||||||
|
assertThat(response).containsEntry("error", "custom error");
|
||||||
|
assertThat(response).containsEntry("custom", "value");
|
||||||
|
assertThat(response).doesNotContainKey("path");
|
||||||
|
assertThat(response).containsKey("timestamp");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void errorResponseWithDefaultErrorAttributesSubclassUsingDeprecatedApiWithoutDelegation() {
|
||||||
|
ErrorAttributes attributes = new DefaultErrorAttributes() {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("error", "custom error");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ManagementErrorEndpoint endpoint = new ManagementErrorEndpoint(attributes, this.errorProperties);
|
||||||
|
Map<String, Object> response = endpoint.invoke(new ServletWebRequest(new MockHttpServletRequest()));
|
||||||
|
assertThat(response).hasSize(1);
|
||||||
|
assertThat(response).containsEntry("error", "custom error");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ 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.ResourceProperties;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
@@ -134,26 +136,23 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
|||||||
* @param includeStackTrace whether to include the error stacktrace information
|
* @param includeStackTrace whether to include the error stacktrace information
|
||||||
* @return the error attributes as a Map
|
* @return the error attributes as a Map
|
||||||
* @deprecated since 2.3.0 in favor of
|
* @deprecated since 2.3.0 in favor of
|
||||||
* {@link #getErrorAttributes(ServerRequest, boolean, boolean, boolean)}
|
* {@link #getErrorAttributes(ServerRequest, ErrorAttributeOptions)}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||||
return this.errorAttributes.getErrorAttributes(request, includeStackTrace, false, false);
|
return getErrorAttributes(request,
|
||||||
|
(includeStackTrace) ? ErrorAttributeOptions.of(Include.STACK_TRACE) : ErrorAttributeOptions.defaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the error attributes from the current request, to be used to populate error
|
* Extract the error attributes from the current request, to be used to populate error
|
||||||
* views or JSON payloads.
|
* views or JSON payloads.
|
||||||
* @param request the source request
|
* @param request the source request
|
||||||
* @param includeStackTrace whether to include the stacktrace attribute
|
* @param options options to control error attributes
|
||||||
* @param includeMessage whether to include the message attribute
|
|
||||||
* @param includeBindingErrors whether to include the errors attribute
|
|
||||||
* @return the error attributes as a Map
|
* @return the error attributes as a Map
|
||||||
*/
|
*/
|
||||||
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace,
|
protected Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
|
||||||
boolean includeMessage, boolean includeBindingErrors) {
|
return this.errorAttributes.getErrorAttributes(request, options);
|
||||||
return this.errorAttributes.getErrorAttributes(request, includeStackTrace, includeMessage,
|
|
||||||
includeBindingErrors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ 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.ResourceProperties;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
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;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -113,11 +115,7 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
|||||||
* @return a {@code Publisher} of the HTTP response
|
* @return a {@code Publisher} of the HTTP response
|
||||||
*/
|
*/
|
||||||
protected Mono<ServerResponse> renderErrorView(ServerRequest request) {
|
protected Mono<ServerResponse> renderErrorView(ServerRequest request) {
|
||||||
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.TEXT_HTML);
|
Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML));
|
||||||
boolean includeMessage = isIncludeMessage(request, MediaType.TEXT_HTML);
|
|
||||||
boolean includeBindingErrors = isIncludeBindingErrors(request, MediaType.TEXT_HTML);
|
|
||||||
Map<String, Object> error = getErrorAttributes(request, includeStackTrace, includeMessage,
|
|
||||||
includeBindingErrors);
|
|
||||||
int errorStatus = getHttpStatus(error);
|
int errorStatus = getHttpStatus(error);
|
||||||
ServerResponse.BodyBuilder responseBody = ServerResponse.status(errorStatus).contentType(TEXT_HTML_UTF8);
|
ServerResponse.BodyBuilder responseBody = ServerResponse.status(errorStatus).contentType(TEXT_HTML_UTF8);
|
||||||
return Flux.just(getData(errorStatus).toArray(new String[] {}))
|
return Flux.just(getData(errorStatus).toArray(new String[] {}))
|
||||||
@@ -144,15 +142,28 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
|||||||
* @return a {@code Publisher} of the HTTP response
|
* @return a {@code Publisher} of the HTTP response
|
||||||
*/
|
*/
|
||||||
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
|
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
|
||||||
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
|
Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
|
||||||
boolean includeMessage = isIncludeMessage(request, MediaType.ALL);
|
|
||||||
boolean includeBindingErrors = isIncludeBindingErrors(request, MediaType.ALL);
|
|
||||||
Map<String, Object> error = getErrorAttributes(request, includeStackTrace, includeMessage,
|
|
||||||
includeBindingErrors);
|
|
||||||
return ServerResponse.status(getHttpStatus(error)).contentType(MediaType.APPLICATION_JSON)
|
return ServerResponse.status(getHttpStatus(error)).contentType(MediaType.APPLICATION_JSON)
|
||||||
.body(BodyInserters.fromValue(error));
|
.body(BodyInserters.fromValue(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ErrorAttributeOptions getErrorAttributeOptions(ServerRequest request, MediaType mediaType) {
|
||||||
|
ErrorAttributeOptions options = ErrorAttributeOptions.defaults();
|
||||||
|
if (this.errorProperties.isIncludeException()) {
|
||||||
|
options = options.including(Include.EXCEPTION);
|
||||||
|
}
|
||||||
|
if (isIncludeStackTrace(request, mediaType)) {
|
||||||
|
options = options.including(Include.STACK_TRACE);
|
||||||
|
}
|
||||||
|
if (isIncludeMessage(request, mediaType)) {
|
||||||
|
options = options.including(Include.MESSAGE);
|
||||||
|
}
|
||||||
|
if (isIncludeBindingErrors(request, mediaType)) {
|
||||||
|
options = options.including(Include.BINDING_ERRORS);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the stacktrace attribute should be included.
|
* Determine if the stacktrace attribute should be included.
|
||||||
* @param request the source request
|
* @param request the source request
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2019 the original author or authors.
|
* Copyright 2012-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -45,6 +45,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
|||||||
* {@link org.springframework.web.server.WebExceptionHandler}.
|
* {@link org.springframework.web.server.WebExceptionHandler}.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@@ -77,7 +78,7 @@ public class ErrorWebFluxAutoConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
|
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
|
||||||
public DefaultErrorAttributes errorAttributes() {
|
public DefaultErrorAttributes errorAttributes() {
|
||||||
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
|
return new DefaultErrorAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import javax.servlet.RequestDispatcher;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
@@ -74,18 +76,17 @@ public abstract class AbstractErrorController implements ErrorController {
|
|||||||
* @param includeStackTrace if stack trace elements should be included
|
* @param includeStackTrace if stack trace elements should be included
|
||||||
* @return the error attributes
|
* @return the error attributes
|
||||||
* @deprecated since 2.3.0 in favor of
|
* @deprecated since 2.3.0 in favor of
|
||||||
* {@link #getErrorAttributes(HttpServletRequest, boolean, boolean, boolean)}
|
* {@link #getErrorAttributes(HttpServletRequest, ErrorAttributeOptions)}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
||||||
return getErrorAttributes(request, includeStackTrace, false, false);
|
return getErrorAttributes(request,
|
||||||
|
(includeStackTrace) ? ErrorAttributeOptions.of(Include.STACK_TRACE) : ErrorAttributeOptions.defaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace,
|
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, ErrorAttributeOptions options) {
|
||||||
boolean includeMessage, boolean includeBindingErrors) {
|
|
||||||
WebRequest webRequest = new ServletWebRequest(request);
|
WebRequest webRequest = new ServletWebRequest(request);
|
||||||
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace, includeMessage,
|
return this.errorAttributes.getErrorAttributes(webRequest, options);
|
||||||
includeBindingErrors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean getTraceParameter(HttpServletRequest request) {
|
protected boolean getTraceParameter(HttpServletRequest request) {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -87,9 +89,8 @@ public class BasicErrorController extends AbstractErrorController {
|
|||||||
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
|
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
|
||||||
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
|
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
|
||||||
HttpStatus status = getStatus(request);
|
HttpStatus status = getStatus(request);
|
||||||
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request,
|
Map<String, Object> model = Collections
|
||||||
isIncludeStackTrace(request, MediaType.TEXT_HTML), isIncludeMessage(request, MediaType.TEXT_HTML),
|
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
|
||||||
isIncludeBindingErrors(request, MediaType.TEXT_HTML)));
|
|
||||||
response.setStatus(status.value());
|
response.setStatus(status.value());
|
||||||
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
|
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
|
||||||
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
|
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
|
||||||
@@ -101,8 +102,7 @@ public class BasicErrorController extends AbstractErrorController {
|
|||||||
if (status == HttpStatus.NO_CONTENT) {
|
if (status == HttpStatus.NO_CONTENT) {
|
||||||
return new ResponseEntity<>(status);
|
return new ResponseEntity<>(status);
|
||||||
}
|
}
|
||||||
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL),
|
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
|
||||||
isIncludeMessage(request, MediaType.ALL), isIncludeBindingErrors(request, MediaType.TEXT_HTML));
|
|
||||||
return new ResponseEntity<>(body, status);
|
return new ResponseEntity<>(body, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +112,23 @@ public class BasicErrorController extends AbstractErrorController {
|
|||||||
return ResponseEntity.status(status).build();
|
return ResponseEntity.status(status).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ErrorAttributeOptions getErrorAttributeOptions(HttpServletRequest request, MediaType mediaType) {
|
||||||
|
ErrorAttributeOptions options = ErrorAttributeOptions.defaults();
|
||||||
|
if (this.errorProperties.isIncludeException()) {
|
||||||
|
options = options.including(Include.EXCEPTION);
|
||||||
|
}
|
||||||
|
if (isIncludeStackTrace(request, mediaType)) {
|
||||||
|
options = options.including(Include.STACK_TRACE);
|
||||||
|
}
|
||||||
|
if (isIncludeMessage(request, mediaType)) {
|
||||||
|
options = options.including(Include.MESSAGE);
|
||||||
|
}
|
||||||
|
if (isIncludeBindingErrors(request, mediaType)) {
|
||||||
|
options = options.including(Include.BINDING_ERRORS);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the stacktrace attribute should be included.
|
* Determine if the stacktrace attribute should be included.
|
||||||
* @param request the source request
|
* @param request the source request
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2019 the original author or authors.
|
* Copyright 2012-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -81,6 +81,7 @@ import org.springframework.web.util.HtmlUtils;
|
|||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@@ -100,7 +101,7 @@ public class ErrorMvcAutoConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
|
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
|
||||||
public DefaultErrorAttributes errorAttributes() {
|
public DefaultErrorAttributes errorAttributes() {
|
||||||
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
|
return new DefaultErrorAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
package org.springframework.boot.autoconfigure.web.reactive.error;
|
package org.springframework.boot.autoconfigure.web.reactive.error;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
@@ -34,6 +36,9 @@ import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplic
|
|||||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||||
import org.springframework.boot.test.system.CapturedOutput;
|
import org.springframework.boot.test.system.CapturedOutput;
|
||||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||||
|
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
|
||||||
|
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -43,6 +48,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import org.springframework.web.server.WebFilter;
|
import org.springframework.web.server.WebFilter;
|
||||||
@@ -64,7 +70,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
|||||||
|
|
||||||
private final LogIdFilter logIdFilter = new LogIdFilter();
|
private final LogIdFilter logIdFilter = new LogIdFilter();
|
||||||
|
|
||||||
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||||
.withConfiguration(AutoConfigurations.of(ReactiveWebServerFactoryAutoConfiguration.class,
|
.withConfiguration(AutoConfigurations.of(ReactiveWebServerFactoryAutoConfiguration.class,
|
||||||
HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class,
|
HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class,
|
||||||
ErrorWebFluxAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
|
ErrorWebFluxAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
|
||||||
@@ -343,6 +349,26 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultErrorAttributesSubclassUsingDeprecatedApiAndDelegation() {
|
||||||
|
this.contextRunner.withUserConfiguration(CustomErrorAttributesWithDelegation.class).run((context) -> {
|
||||||
|
WebTestClient client = getWebClient(context);
|
||||||
|
client.get().uri("/badRequest").exchange().expectStatus().isBadRequest().expectBody().jsonPath("status")
|
||||||
|
.isEqualTo("400").jsonPath("error").isEqualTo("custom error").jsonPath("newAttribute")
|
||||||
|
.isEqualTo("value").jsonPath("path").doesNotExist();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultErrorAttributesSubclassUsingDeprecatedApiWithoutDelegation() {
|
||||||
|
this.contextRunner.withUserConfiguration(CustomErrorAttributesWithoutDelegation.class).run((context) -> {
|
||||||
|
WebTestClient client = getWebClient(context);
|
||||||
|
client.get().uri("/badRequest").exchange().expectStatus().isBadRequest().expectBody().jsonPath("status")
|
||||||
|
.isEqualTo("400").jsonPath("timestamp").doesNotExist().jsonPath("error").isEqualTo("custom error")
|
||||||
|
.jsonPath("path").doesNotExist();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private String getErrorTemplatesLocation() {
|
private String getErrorTemplatesLocation() {
|
||||||
String packageName = getClass().getPackage().getName();
|
String packageName = getClass().getPackage().getName();
|
||||||
return "classpath:/" + packageName.replace('.', '/') + "/templates/";
|
return "classpath:/" + packageName.replace('.', '/') + "/templates/";
|
||||||
@@ -405,4 +431,45 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class CustomErrorAttributesWithDelegation {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ErrorAttributes errorAttributes() {
|
||||||
|
return new DefaultErrorAttributes() {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||||
|
Map<String, Object> errorAttributes = super.getErrorAttributes(request, includeStackTrace);
|
||||||
|
errorAttributes.put("error", "custom error");
|
||||||
|
errorAttributes.put("newAttribute", "value");
|
||||||
|
errorAttributes.remove("path");
|
||||||
|
return errorAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class CustomErrorAttributesWithoutDelegation {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ErrorAttributes errorAttributes() {
|
||||||
|
return new DefaultErrorAttributes() {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||||
|
Map<String, Object> errorAttributes = new HashMap<>();
|
||||||
|
errorAttributes.put("status", 400);
|
||||||
|
errorAttributes.put("error", "custom error");
|
||||||
|
return errorAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
@@ -66,8 +65,7 @@ class DefaultErrorWebExceptionHandlerTests {
|
|||||||
ResourceProperties resourceProperties = new ResourceProperties();
|
ResourceProperties resourceProperties = new ResourceProperties();
|
||||||
ErrorProperties errorProperties = new ErrorProperties();
|
ErrorProperties errorProperties = new ErrorProperties();
|
||||||
ApplicationContext context = new AnnotationConfigReactiveWebApplicationContext();
|
ApplicationContext context = new AnnotationConfigReactiveWebApplicationContext();
|
||||||
given(errorAttributes.getErrorAttributes(any(), anyBoolean(), anyBoolean(), anyBoolean()))
|
given(errorAttributes.getErrorAttributes(any(), any())).willReturn(getErrorAttributes());
|
||||||
.willReturn(getErrorAttributes());
|
|
||||||
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
|
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
|
||||||
resourceProperties, errorProperties, context);
|
resourceProperties, errorProperties, context);
|
||||||
setupViewResolver(exceptionHandler);
|
setupViewResolver(exceptionHandler);
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoC
|
|||||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
import org.springframework.boot.test.system.CapturedOutput;
|
import org.springframework.boot.test.system.CapturedOutput;
|
||||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
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.servlet.error.ErrorAttributes;
|
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
@@ -37,11 +39,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
* Tests for {@link ErrorMvcAutoConfiguration}.
|
* Tests for {@link ErrorMvcAutoConfiguration}.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
@ExtendWith(OutputCaptureExtension.class)
|
@ExtendWith(OutputCaptureExtension.class)
|
||||||
class ErrorMvcAutoConfigurationTests {
|
class ErrorMvcAutoConfigurationTests {
|
||||||
|
|
||||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner().withConfiguration(
|
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner().withConfiguration(
|
||||||
AutoConfigurations.of(DispatcherServletAutoConfiguration.class, ErrorMvcAutoConfiguration.class));
|
AutoConfigurations.of(DispatcherServletAutoConfiguration.class, ErrorMvcAutoConfiguration.class));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -51,7 +54,7 @@ class ErrorMvcAutoConfigurationTests {
|
|||||||
ErrorAttributes errorAttributes = context.getBean(ErrorAttributes.class);
|
ErrorAttributes errorAttributes = context.getBean(ErrorAttributes.class);
|
||||||
DispatcherServletWebRequest webRequest = createWebRequest(new IllegalStateException("Exception message"),
|
DispatcherServletWebRequest webRequest = createWebRequest(new IllegalStateException("Exception message"),
|
||||||
false);
|
false);
|
||||||
errorView.render(errorAttributes.getErrorAttributes(webRequest, true, true, true), webRequest.getRequest(),
|
errorView.render(errorAttributes.getErrorAttributes(webRequest, withAllOptions()), webRequest.getRequest(),
|
||||||
webRequest.getResponse());
|
webRequest.getResponse());
|
||||||
assertThat(webRequest.getResponse().getContentType()).isEqualTo("text/html;charset=UTF-8");
|
assertThat(webRequest.getResponse().getContentType()).isEqualTo("text/html;charset=UTF-8");
|
||||||
String responseString = ((MockHttpServletResponse) webRequest.getResponse()).getContentAsString();
|
String responseString = ((MockHttpServletResponse) webRequest.getResponse()).getContentAsString();
|
||||||
@@ -69,7 +72,7 @@ class ErrorMvcAutoConfigurationTests {
|
|||||||
ErrorAttributes errorAttributes = context.getBean(ErrorAttributes.class);
|
ErrorAttributes errorAttributes = context.getBean(ErrorAttributes.class);
|
||||||
DispatcherServletWebRequest webRequest = createWebRequest(new IllegalStateException("Exception message"),
|
DispatcherServletWebRequest webRequest = createWebRequest(new IllegalStateException("Exception message"),
|
||||||
true);
|
true);
|
||||||
errorView.render(errorAttributes.getErrorAttributes(webRequest, true, true, true), webRequest.getRequest(),
|
errorView.render(errorAttributes.getErrorAttributes(webRequest, withAllOptions()), webRequest.getRequest(),
|
||||||
webRequest.getResponse());
|
webRequest.getResponse());
|
||||||
assertThat(output).contains("Cannot render error page for request [/path] "
|
assertThat(output).contains("Cannot render error page for request [/path] "
|
||||||
+ "and exception [Exception message] as the response has "
|
+ "and exception [Exception message] as the response has "
|
||||||
@@ -89,4 +92,9 @@ class ErrorMvcAutoConfigurationTests {
|
|||||||
return webRequest;
|
return webRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ErrorAttributeOptions withAllOptions() {
|
||||||
|
return ErrorAttributeOptions.of(Include.EXCEPTION, Include.STACK_TRACE, Include.MESSAGE,
|
||||||
|
Include.BINDING_ERRORS);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* 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.web.error;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options controlling the contents of {@code ErrorAttributes}.
|
||||||
|
*
|
||||||
|
* @author Scott Frederick
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
public final class ErrorAttributeOptions {
|
||||||
|
|
||||||
|
private final Set<Include> includes;
|
||||||
|
|
||||||
|
private ErrorAttributeOptions(Set<Include> includes) {
|
||||||
|
this.includes = includes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the option for including the specified attribute in the error response.
|
||||||
|
* @param include error attribute to get
|
||||||
|
* @return {@code true} if the {@code Include} attribute is included in the error
|
||||||
|
* response, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public boolean isIncluded(Include include) {
|
||||||
|
return this.includes.contains(include);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all options for including attributes in the error response.
|
||||||
|
* @return {@code true} if the {@code Include} attribute is included in the error
|
||||||
|
* response, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public Set<Include> getIncludes() {
|
||||||
|
return this.includes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an {@code ErrorAttributeOptions} that includes the specified attribute
|
||||||
|
* {@link Include} options.
|
||||||
|
* @param includes error attributes to include
|
||||||
|
* @return an {@code ErrorAttributeOptions}
|
||||||
|
*/
|
||||||
|
public ErrorAttributeOptions including(Include... includes) {
|
||||||
|
EnumSet<Include> updated = (this.includes.isEmpty()) ? EnumSet.noneOf(Include.class)
|
||||||
|
: EnumSet.copyOf(this.includes);
|
||||||
|
updated.addAll(Arrays.asList(includes));
|
||||||
|
return new ErrorAttributeOptions(Collections.unmodifiableSet(updated));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an {@code ErrorAttributeOptions} that excludes the specified attribute
|
||||||
|
* {@link Include} options.
|
||||||
|
* @param excludes error attributes to exclude
|
||||||
|
* @return an {@code ErrorAttributeOptions}
|
||||||
|
*/
|
||||||
|
public ErrorAttributeOptions excluding(Include... excludes) {
|
||||||
|
EnumSet<Include> updated = EnumSet.copyOf(this.includes);
|
||||||
|
updated.removeAll(Arrays.asList(excludes));
|
||||||
|
return new ErrorAttributeOptions(Collections.unmodifiableSet(updated));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@code ErrorAttributeOptions} with defaults.
|
||||||
|
* @return an {@code ErrorAttributeOptions}
|
||||||
|
*/
|
||||||
|
public static ErrorAttributeOptions defaults() {
|
||||||
|
return of();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@code ErrorAttributeOptions} that includes the specified attribute
|
||||||
|
* {@link Include} options.
|
||||||
|
* @param includes error attributes to include
|
||||||
|
* @return an {@code ErrorAttributeOptions}
|
||||||
|
*/
|
||||||
|
public static ErrorAttributeOptions of(Include... includes) {
|
||||||
|
return of(Arrays.asList(includes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@code ErrorAttributeOptions} that includes the specified attribute
|
||||||
|
* {@link Include} options.
|
||||||
|
* @param includes error attributes to include
|
||||||
|
* @return an {@code ErrorAttributeOptions}
|
||||||
|
*/
|
||||||
|
public static ErrorAttributeOptions of(Collection<Include> includes) {
|
||||||
|
return new ErrorAttributeOptions(
|
||||||
|
(includes.isEmpty()) ? Collections.emptySet() : Collections.unmodifiableSet(EnumSet.copyOf(includes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error attributes that can be included in an error response.
|
||||||
|
*/
|
||||||
|
public enum Include {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include the exception class name attribute.
|
||||||
|
*/
|
||||||
|
EXCEPTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include the stack trace attribute.
|
||||||
|
*/
|
||||||
|
STACK_TRACE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include the message attribute.
|
||||||
|
*/
|
||||||
|
MESSAGE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include the binding errors attribute.
|
||||||
|
*/
|
||||||
|
BINDING_ERRORS
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Web error handling infrastructure.
|
||||||
|
*/
|
||||||
|
package org.springframework.boot.web.error;
|
||||||
@@ -22,6 +22,8 @@ import java.util.Date;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.core.annotation.MergedAnnotation;
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
import org.springframework.core.annotation.MergedAnnotations;
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||||
@@ -42,9 +44,10 @@ import org.springframework.web.server.ServerWebExchange;
|
|||||||
* <li>status - The status code</li>
|
* <li>status - The status code</li>
|
||||||
* <li>error - The error reason</li>
|
* <li>error - The error reason</li>
|
||||||
* <li>exception - The class name of the root exception (if configured)</li>
|
* <li>exception - The class name of the root exception (if configured)</li>
|
||||||
* <li>message - The exception message</li>
|
* <li>message - The exception message (if configured)</li>
|
||||||
* <li>errors - Any {@link ObjectError}s from a {@link BindingResult} exception
|
* <li>errors - Any {@link ObjectError}s from a {@link BindingResult} exception (if
|
||||||
* <li>trace - The exception stack trace</li>
|
* configured)</li>
|
||||||
|
* <li>trace - The exception stack trace (if configured)</li>
|
||||||
* <li>path - The URL path when the exception was raised</li>
|
* <li>path - The URL path when the exception was raised</li>
|
||||||
* <li>requestId - Unique ID associated with the current request</li>
|
* <li>requestId - Unique ID associated with the current request</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
@@ -60,20 +63,22 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
|||||||
|
|
||||||
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR";
|
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR";
|
||||||
|
|
||||||
private final boolean includeException;
|
private final Boolean includeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link DefaultErrorAttributes} instance that does not include the
|
* Create a new {@link DefaultErrorAttributes} instance.
|
||||||
* "exception" attribute.
|
|
||||||
*/
|
*/
|
||||||
public DefaultErrorAttributes() {
|
public DefaultErrorAttributes() {
|
||||||
this(false);
|
this.includeException = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link DefaultErrorAttributes} instance.
|
* Create a new {@link DefaultErrorAttributes} instance.
|
||||||
* @param includeException whether to include the "exception" attribute
|
* @param includeException whether to include the "exception" attribute
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link ErrorAttributeOptions#including(Include...)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public DefaultErrorAttributes(boolean includeException) {
|
public DefaultErrorAttributes(boolean includeException) {
|
||||||
this.includeException = includeException;
|
this.includeException = includeException;
|
||||||
}
|
}
|
||||||
@@ -81,12 +86,6 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
|||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||||
return getErrorAttributes(request, includeStackTrace, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace,
|
|
||||||
boolean includeMessage, boolean includeBindingErrors) {
|
|
||||||
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
||||||
errorAttributes.put("timestamp", new Date());
|
errorAttributes.put("timestamp", new Date());
|
||||||
errorAttributes.put("path", request.path());
|
errorAttributes.put("path", request.path());
|
||||||
@@ -96,9 +95,30 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
|||||||
HttpStatus errorStatus = determineHttpStatus(error, responseStatusAnnotation);
|
HttpStatus errorStatus = determineHttpStatus(error, responseStatusAnnotation);
|
||||||
errorAttributes.put("status", errorStatus.value());
|
errorAttributes.put("status", errorStatus.value());
|
||||||
errorAttributes.put("error", errorStatus.getReasonPhrase());
|
errorAttributes.put("error", errorStatus.getReasonPhrase());
|
||||||
errorAttributes.put("message", determineMessage(error, responseStatusAnnotation, includeMessage));
|
errorAttributes.put("message", determineMessage(error, responseStatusAnnotation));
|
||||||
errorAttributes.put("requestId", request.exchange().getRequest().getId());
|
errorAttributes.put("requestId", request.exchange().getRequest().getId());
|
||||||
handleException(errorAttributes, determineException(error), includeStackTrace, includeBindingErrors);
|
handleException(errorAttributes, determineException(error));
|
||||||
|
return errorAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
|
||||||
|
Map<String, Object> errorAttributes = getErrorAttributes(request, options.isIncluded(Include.STACK_TRACE));
|
||||||
|
if (this.includeException != null) {
|
||||||
|
options = options.including(Include.EXCEPTION);
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.EXCEPTION)) {
|
||||||
|
errorAttributes.remove("exception");
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.STACK_TRACE)) {
|
||||||
|
errorAttributes.remove("trace");
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
|
||||||
|
errorAttributes.put("message", "");
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.BINDING_ERRORS)) {
|
||||||
|
errorAttributes.remove("errors");
|
||||||
|
}
|
||||||
return errorAttributes;
|
return errorAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,11 +129,7 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
|||||||
return responseStatusAnnotation.getValue("code", HttpStatus.class).orElse(HttpStatus.INTERNAL_SERVER_ERROR);
|
return responseStatusAnnotation.getValue("code", HttpStatus.class).orElse(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String determineMessage(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation,
|
private String determineMessage(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
|
||||||
boolean includeMessage) {
|
|
||||||
if (!includeMessage) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (error instanceof BindingResult) {
|
if (error instanceof BindingResult) {
|
||||||
return error.getMessage();
|
return error.getMessage();
|
||||||
}
|
}
|
||||||
@@ -141,15 +157,10 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
|||||||
errorAttributes.put("trace", stackTrace.toString());
|
errorAttributes.put("trace", stackTrace.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleException(Map<String, Object> errorAttributes, Throwable error, boolean includeStackTrace,
|
private void handleException(Map<String, Object> errorAttributes, Throwable error) {
|
||||||
boolean includeBindingErrors) {
|
errorAttributes.put("exception", error.getClass().getName());
|
||||||
if (this.includeException) {
|
addStackTrace(errorAttributes, error);
|
||||||
errorAttributes.put("exception", error.getClass().getName());
|
if (error instanceof BindingResult) {
|
||||||
}
|
|
||||||
if (includeStackTrace) {
|
|
||||||
addStackTrace(errorAttributes, error);
|
|
||||||
}
|
|
||||||
if (includeBindingErrors && (error instanceof BindingResult)) {
|
|
||||||
BindingResult result = (BindingResult) error;
|
BindingResult result = (BindingResult) error;
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
errorAttributes.put("errors", result.getAllErrors());
|
errorAttributes.put("errors", result.getAllErrors());
|
||||||
|
|||||||
@@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.boot.web.reactive.error;
|
package org.springframework.boot.web.reactive.error;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
@@ -39,21 +42,23 @@ public interface ErrorAttributes {
|
|||||||
* @param includeStackTrace if stack trace attribute should be included
|
* @param includeStackTrace if stack trace attribute should be included
|
||||||
* @return a map of error attributes
|
* @return a map of error attributes
|
||||||
* @deprecated since 2.3.0 in favor of
|
* @deprecated since 2.3.0 in favor of
|
||||||
* {@link #getErrorAttributes(ServerRequest, boolean, boolean, boolean)}
|
* {@link #getErrorAttributes(ServerRequest, ErrorAttributeOptions)}
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace);
|
@Deprecated
|
||||||
|
default Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@link Map} of the error attributes. The map can be used as the model of
|
* Return a {@link Map} of the error attributes. The map can be used as the model of
|
||||||
* an error page, or returned as a {@link ServerResponse} body.
|
* an error page, or returned as a {@link ServerResponse} body.
|
||||||
* @param request the source request
|
* @param request the source request
|
||||||
* @param includeStackTrace if stack trace attribute should be included
|
* @param options options for error attribute contents
|
||||||
* @param includeMessage if message attribute should be included
|
|
||||||
* @param includeBindingErrors if errors attribute should be included
|
|
||||||
* @return a map of error attributes
|
* @return a map of error attributes
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace, boolean includeMessage,
|
default Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
|
||||||
boolean includeBindingErrors);
|
return getErrorAttributes(request, options.isIncluded(Include.STACK_TRACE));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the underlying cause of the error or {@code null} if the error cannot be
|
* Return the underlying cause of the error or {@code null} if the error cannot be
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import javax.servlet.ServletException;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -67,20 +69,22 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
|||||||
|
|
||||||
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR";
|
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR";
|
||||||
|
|
||||||
private final boolean includeException;
|
private final Boolean includeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link DefaultErrorAttributes} instance that does not include the
|
* Create a new {@link DefaultErrorAttributes} instance.
|
||||||
* "exception" attribute.
|
|
||||||
*/
|
*/
|
||||||
public DefaultErrorAttributes() {
|
public DefaultErrorAttributes() {
|
||||||
this(false);
|
this.includeException = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link DefaultErrorAttributes} instance.
|
* Create a new {@link DefaultErrorAttributes} instance.
|
||||||
* @param includeException whether to include the "exception" attribute
|
* @param includeException whether to include the "exception" attribute
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link ErrorAttributeOptions#including(Include...)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public DefaultErrorAttributes(boolean includeException) {
|
public DefaultErrorAttributes(boolean includeException) {
|
||||||
this.includeException = includeException;
|
this.includeException = includeException;
|
||||||
}
|
}
|
||||||
@@ -104,20 +108,35 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
|||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||||
return getErrorAttributes(webRequest, includeStackTrace, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace,
|
|
||||||
boolean includeMessage, boolean includeBindingErrors) {
|
|
||||||
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
||||||
errorAttributes.put("timestamp", new Date());
|
errorAttributes.put("timestamp", new Date());
|
||||||
addStatus(errorAttributes, webRequest);
|
addStatus(errorAttributes, webRequest);
|
||||||
addErrorDetails(errorAttributes, webRequest, includeStackTrace, includeMessage, includeBindingErrors);
|
addErrorDetails(errorAttributes, webRequest);
|
||||||
addPath(errorAttributes, webRequest);
|
addPath(errorAttributes, webRequest);
|
||||||
return errorAttributes;
|
return errorAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
|
||||||
|
Map<String, Object> errorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));
|
||||||
|
if (this.includeException != null) {
|
||||||
|
options = options.including(Include.EXCEPTION);
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.EXCEPTION)) {
|
||||||
|
errorAttributes.remove("exception");
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.STACK_TRACE)) {
|
||||||
|
errorAttributes.remove("trace");
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
|
||||||
|
errorAttributes.put("message", "");
|
||||||
|
}
|
||||||
|
if (!options.isIncluded(Include.BINDING_ERRORS)) {
|
||||||
|
errorAttributes.remove("errors");
|
||||||
|
}
|
||||||
|
return errorAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
|
private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
|
||||||
Integer status = getAttribute(requestAttributes, RequestDispatcher.ERROR_STATUS_CODE);
|
Integer status = getAttribute(requestAttributes, RequestDispatcher.ERROR_STATUS_CODE);
|
||||||
if (status == null) {
|
if (status == null) {
|
||||||
@@ -135,40 +154,29 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest, boolean includeStackTrace,
|
private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest) {
|
||||||
boolean includeMessage, boolean includeBindingErrors) {
|
|
||||||
Throwable error = getError(webRequest);
|
Throwable error = getError(webRequest);
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
while (error instanceof ServletException && error.getCause() != null) {
|
while (error instanceof ServletException && error.getCause() != null) {
|
||||||
error = error.getCause();
|
error = error.getCause();
|
||||||
}
|
}
|
||||||
if (this.includeException) {
|
errorAttributes.put("exception", error.getClass().getName());
|
||||||
errorAttributes.put("exception", error.getClass().getName());
|
addStackTrace(errorAttributes, error);
|
||||||
}
|
|
||||||
if (includeStackTrace) {
|
|
||||||
addStackTrace(errorAttributes, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
addErrorMessage(errorAttributes, webRequest, error, includeMessage, includeBindingErrors);
|
addErrorMessage(errorAttributes, webRequest, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error,
|
private void addErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error) {
|
||||||
boolean includeMessage, boolean includeBindingErrors) {
|
|
||||||
BindingResult result = extractBindingResult(error);
|
BindingResult result = extractBindingResult(error);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
addExceptionErrorMessage(errorAttributes, webRequest, error, includeMessage);
|
addExceptionErrorMessage(errorAttributes, webRequest, error);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
addBindingResultErrorMessage(errorAttributes, result, includeMessage, includeBindingErrors);
|
addBindingResultErrorMessage(errorAttributes, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addExceptionErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error,
|
private void addExceptionErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error) {
|
||||||
boolean includeMessage) {
|
|
||||||
if (!includeMessage) {
|
|
||||||
errorAttributes.put("message", "");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object message = getAttribute(webRequest, RequestDispatcher.ERROR_MESSAGE);
|
Object message = getAttribute(webRequest, RequestDispatcher.ERROR_MESSAGE);
|
||||||
if (StringUtils.isEmpty(message) && error != null) {
|
if (StringUtils.isEmpty(message) && error != null) {
|
||||||
message = error.getMessage();
|
message = error.getMessage();
|
||||||
@@ -179,13 +187,10 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
|||||||
errorAttributes.put("message", message);
|
errorAttributes.put("message", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBindingResultErrorMessage(Map<String, Object> errorAttributes, BindingResult result,
|
private void addBindingResultErrorMessage(Map<String, Object> errorAttributes, BindingResult result) {
|
||||||
boolean includeMessage, boolean includeBindingErrors) {
|
errorAttributes.put("message", "Validation failed for object='" + result.getObjectName() + "'. "
|
||||||
errorAttributes.put("message", (includeMessage) ? "Validation failed for object='" + result.getObjectName()
|
+ "Error count: " + result.getErrorCount());
|
||||||
+ "'. " + "Error count: " + result.getErrorCount() : "");
|
errorAttributes.put("errors", result.getAllErrors());
|
||||||
if (includeBindingErrors && result.hasErrors()) {
|
|
||||||
errorAttributes.put("errors", result.getAllErrors());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BindingResult extractBindingResult(Throwable error) {
|
private BindingResult extractBindingResult(Throwable error) {
|
||||||
|
|||||||
@@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.boot.web.servlet.error;
|
package org.springframework.boot.web.servlet.error;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.context.request.WebRequest;
|
import org.springframework.web.context.request.WebRequest;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
@@ -40,24 +43,25 @@ public interface ErrorAttributes {
|
|||||||
* @param includeStackTrace if stack trace element should be included
|
* @param includeStackTrace if stack trace element should be included
|
||||||
* @return a map of error attributes
|
* @return a map of error attributes
|
||||||
* @deprecated since 2.3.0 in favor of
|
* @deprecated since 2.3.0 in favor of
|
||||||
* {@link #getErrorAttributes(WebRequest, boolean, boolean, boolean)}
|
* {@link #getErrorAttributes(WebRequest, ErrorAttributeOptions)}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace);
|
default Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link Map} of the error attributes. The map can be used as the model of
|
* Returns a {@link Map} of the error attributes. The map can be used as the model of
|
||||||
* an error page {@link ModelAndView}, or returned as a
|
* an error page {@link ModelAndView}, or returned as a
|
||||||
* {@link ResponseBody @ResponseBody}.
|
* {@link ResponseBody @ResponseBody}.
|
||||||
* @param webRequest the source request
|
* @param webRequest the source request
|
||||||
* @param includeStackTrace if stack trace element should be included
|
* @param options options for error attribute contents
|
||||||
* @param includeMessage if message element should be included
|
|
||||||
* @param includeBindingErrors if errors element should be included
|
|
||||||
* @return a map of error attributes
|
* @return a map of error attributes
|
||||||
* @since 2.3.0
|
* @since 2.3.0
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace, boolean includeMessage,
|
default Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
|
||||||
boolean includeBindingErrors);
|
return getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the underlying cause of the error or {@code null} if the error cannot be
|
* Return the underlying cause of the error or {@code null} if the error cannot be
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.codec.HttpMessageReader;
|
import org.springframework.http.codec.HttpMessageReader;
|
||||||
@@ -55,14 +57,14 @@ class DefaultErrorAttributesTests {
|
|||||||
|
|
||||||
private DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes();
|
private DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes();
|
||||||
|
|
||||||
private List<HttpMessageReader<?>> readers = ServerCodecConfigurer.create().getReaders();
|
private final List<HttpMessageReader<?>> readers = ServerCodecConfigurer.create().getReaders();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void missingExceptionAttribute() {
|
void missingExceptionAttribute() {
|
||||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test").build());
|
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test").build());
|
||||||
ServerRequest request = ServerRequest.create(exchange, this.readers);
|
ServerRequest request = ServerRequest.create(exchange, this.readers);
|
||||||
assertThatIllegalStateException()
|
assertThatIllegalStateException()
|
||||||
.isThrownBy(() -> this.errorAttributes.getErrorAttributes(request, false, false, false))
|
.isThrownBy(() -> this.errorAttributes.getErrorAttributes(request, ErrorAttributeOptions.defaults()))
|
||||||
.withMessageContaining("Missing exception attribute in ServerWebExchange");
|
.withMessageContaining("Missing exception attribute in ServerWebExchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ class DefaultErrorAttributesTests {
|
|||||||
void includeTimeStamp() {
|
void includeTimeStamp() {
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
|
||||||
false, false, false);
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("timestamp")).isInstanceOf(Date.class);
|
assertThat(attributes.get("timestamp")).isInstanceOf(Date.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ class DefaultErrorAttributesTests {
|
|||||||
Error error = new OutOfMemoryError("Test error");
|
Error error = new OutOfMemoryError("Test error");
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
||||||
false, false, false);
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("error")).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
|
assertThat(attributes.get("error")).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
|
||||||
assertThat(attributes.get("status")).isEqualTo(500);
|
assertThat(attributes.get("status")).isEqualTo(500);
|
||||||
}
|
}
|
||||||
@@ -89,7 +91,7 @@ class DefaultErrorAttributesTests {
|
|||||||
Exception error = new CustomException();
|
Exception error = new CustomException();
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
||||||
false, false, false);
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("error")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.getReasonPhrase());
|
assertThat(attributes.get("error")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.getReasonPhrase());
|
||||||
assertThat(attributes.get("message")).isEqualTo("");
|
assertThat(attributes.get("message")).isEqualTo("");
|
||||||
assertThat(attributes.get("status")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.value());
|
assertThat(attributes.get("status")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.value());
|
||||||
@@ -100,7 +102,7 @@ class DefaultErrorAttributesTests {
|
|||||||
Exception error = new CustomException("Test Message");
|
Exception error = new CustomException("Test Message");
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
||||||
false, true, false);
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(attributes.get("error")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.getReasonPhrase());
|
assertThat(attributes.get("error")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.getReasonPhrase());
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test Message");
|
assertThat(attributes.get("message")).isEqualTo("Test Message");
|
||||||
assertThat(attributes.get("status")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.value());
|
assertThat(attributes.get("status")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.value());
|
||||||
@@ -111,7 +113,7 @@ class DefaultErrorAttributesTests {
|
|||||||
Exception error = new Custom2Exception();
|
Exception error = new Custom2Exception();
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, error),
|
||||||
false, true, false);
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(attributes.get("error")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.getReasonPhrase());
|
assertThat(attributes.get("error")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.getReasonPhrase());
|
||||||
assertThat(attributes.get("status")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.value());
|
assertThat(attributes.get("status")).isEqualTo(HttpStatus.I_AM_A_TEAPOT.value());
|
||||||
assertThat(attributes.get("message")).isEqualTo("Nope!");
|
assertThat(attributes.get("message")).isEqualTo("Nope!");
|
||||||
@@ -121,7 +123,7 @@ class DefaultErrorAttributesTests {
|
|||||||
void includeStatusCode() {
|
void includeStatusCode() {
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
|
||||||
false, false, false);
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("error")).isEqualTo(HttpStatus.NOT_FOUND.getReasonPhrase());
|
assertThat(attributes.get("error")).isEqualTo(HttpStatus.NOT_FOUND.getReasonPhrase());
|
||||||
assertThat(attributes.get("status")).isEqualTo(404);
|
assertThat(attributes.get("status")).isEqualTo(404);
|
||||||
}
|
}
|
||||||
@@ -131,7 +133,8 @@ class DefaultErrorAttributesTests {
|
|||||||
Error error = new OutOfMemoryError("Test error");
|
Error error = new OutOfMemoryError("Test error");
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
ServerRequest serverRequest = buildServerRequest(request, error);
|
ServerRequest serverRequest = buildServerRequest(request, error);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error);
|
assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error);
|
||||||
assertThat(attributes.get("exception")).isNull();
|
assertThat(attributes.get("exception")).isNull();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test error");
|
assertThat(attributes.get("message")).isEqualTo("Test error");
|
||||||
@@ -142,7 +145,8 @@ class DefaultErrorAttributesTests {
|
|||||||
Error error = new OutOfMemoryError("Test error");
|
Error error = new OutOfMemoryError("Test error");
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
ServerRequest serverRequest = buildServerRequest(request, error);
|
ServerRequest serverRequest = buildServerRequest(request, error);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error);
|
assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error);
|
||||||
assertThat(attributes.get("message")).isEqualTo("");
|
assertThat(attributes.get("message")).isEqualTo("");
|
||||||
}
|
}
|
||||||
@@ -150,10 +154,11 @@ class DefaultErrorAttributesTests {
|
|||||||
@Test
|
@Test
|
||||||
void includeException() {
|
void includeException() {
|
||||||
RuntimeException error = new RuntimeException("Test");
|
RuntimeException error = new RuntimeException("Test");
|
||||||
this.errorAttributes = new DefaultErrorAttributes(true);
|
this.errorAttributes = new DefaultErrorAttributes();
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
ServerRequest serverRequest = buildServerRequest(request, error);
|
ServerRequest serverRequest = buildServerRequest(request, error);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.EXCEPTION, Include.MESSAGE));
|
||||||
assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error);
|
assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error);
|
||||||
assertThat(attributes.get("exception")).isEqualTo(RuntimeException.class.getName());
|
assertThat(attributes.get("exception")).isEqualTo(RuntimeException.class.getName());
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
@@ -163,10 +168,11 @@ class DefaultErrorAttributesTests {
|
|||||||
void processResponseStatusException() {
|
void processResponseStatusException() {
|
||||||
RuntimeException nested = new RuntimeException("Test");
|
RuntimeException nested = new RuntimeException("Test");
|
||||||
ResponseStatusException error = new ResponseStatusException(HttpStatus.BAD_REQUEST, "invalid request", nested);
|
ResponseStatusException error = new ResponseStatusException(HttpStatus.BAD_REQUEST, "invalid request", nested);
|
||||||
this.errorAttributes = new DefaultErrorAttributes(true);
|
this.errorAttributes = new DefaultErrorAttributes();
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
ServerRequest serverRequest = buildServerRequest(request, error);
|
ServerRequest serverRequest = buildServerRequest(request, error);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.EXCEPTION, Include.MESSAGE));
|
||||||
assertThat(attributes.get("status")).isEqualTo(400);
|
assertThat(attributes.get("status")).isEqualTo(400);
|
||||||
assertThat(attributes.get("message")).isEqualTo("invalid request");
|
assertThat(attributes.get("message")).isEqualTo("invalid request");
|
||||||
assertThat(attributes.get("exception")).isEqualTo(RuntimeException.class.getName());
|
assertThat(attributes.get("exception")).isEqualTo(RuntimeException.class.getName());
|
||||||
@@ -177,10 +183,11 @@ class DefaultErrorAttributesTests {
|
|||||||
void processResponseStatusExceptionWithNoNestedCause() {
|
void processResponseStatusExceptionWithNoNestedCause() {
|
||||||
ResponseStatusException error = new ResponseStatusException(HttpStatus.NOT_ACCEPTABLE,
|
ResponseStatusException error = new ResponseStatusException(HttpStatus.NOT_ACCEPTABLE,
|
||||||
"could not process request");
|
"could not process request");
|
||||||
this.errorAttributes = new DefaultErrorAttributes(true);
|
this.errorAttributes = new DefaultErrorAttributes();
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
ServerRequest serverRequest = buildServerRequest(request, error);
|
ServerRequest serverRequest = buildServerRequest(request, error);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.EXCEPTION, Include.MESSAGE));
|
||||||
assertThat(attributes.get("status")).isEqualTo(406);
|
assertThat(attributes.get("status")).isEqualTo(406);
|
||||||
assertThat(attributes.get("message")).isEqualTo("could not process request");
|
assertThat(attributes.get("message")).isEqualTo("could not process request");
|
||||||
assertThat(attributes.get("exception")).isEqualTo(ResponseStatusException.class.getName());
|
assertThat(attributes.get("exception")).isEqualTo(ResponseStatusException.class.getName());
|
||||||
@@ -191,8 +198,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void notIncludeTrace() {
|
void notIncludeTrace() {
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex), false,
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex),
|
||||||
false, false);
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("trace")).isNull();
|
assertThat(attributes.get("trace")).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,8 +207,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void includeTrace() {
|
void includeTrace() {
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex), true,
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex),
|
||||||
false, false);
|
ErrorAttributeOptions.of(Include.STACK_TRACE));
|
||||||
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +216,7 @@ class DefaultErrorAttributesTests {
|
|||||||
void includePath() {
|
void includePath() {
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
|
||||||
false, false, false);
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("path")).isEqualTo("/test");
|
assertThat(attributes.get("path")).isEqualTo("/test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +224,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void includeLogPrefix() {
|
void includeLogPrefix() {
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
ServerRequest serverRequest = buildServerRequest(request, NOT_FOUND);
|
ServerRequest serverRequest = buildServerRequest(request, NOT_FOUND);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("requestId")).isEqualTo(serverRequest.exchange().getRequest().getId());
|
assertThat(attributes.get("requestId")).isEqualTo(serverRequest.exchange().getRequest().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,8 +237,8 @@ class DefaultErrorAttributesTests {
|
|||||||
bindingResult.addError(new ObjectError("c", "d"));
|
bindingResult.addError(new ObjectError("c", "d"));
|
||||||
Exception ex = new WebExchangeBindException(stringParam, bindingResult);
|
Exception ex = new WebExchangeBindException(stringParam, bindingResult);
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex), false,
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex),
|
||||||
true, true);
|
ErrorAttributeOptions.of(Include.MESSAGE, Include.BINDING_ERRORS));
|
||||||
assertThat(attributes.get("message")).asString()
|
assertThat(attributes.get("message")).asString()
|
||||||
.startsWith("Validation failed for argument at index 0 in method: "
|
.startsWith("Validation failed for argument at index 0 in method: "
|
||||||
+ "int org.springframework.boot.web.reactive.error.DefaultErrorAttributesTests"
|
+ "int org.springframework.boot.web.reactive.error.DefaultErrorAttributesTests"
|
||||||
@@ -246,8 +254,8 @@ class DefaultErrorAttributesTests {
|
|||||||
bindingResult.addError(new ObjectError("c", "d"));
|
bindingResult.addError(new ObjectError("c", "d"));
|
||||||
Exception ex = new WebExchangeBindException(stringParam, bindingResult);
|
Exception ex = new WebExchangeBindException(stringParam, bindingResult);
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex), false,
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex),
|
||||||
false, false);
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("message")).isEqualTo("");
|
assertThat(attributes.get("message")).isEqualTo("");
|
||||||
assertThat(attributes.containsKey("errors")).isFalse();
|
assertThat(attributes.containsKey("errors")).isFalse();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.boot.web.servlet.error;
|
package org.springframework.boot.web.servlet.error;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -24,8 +25,12 @@ import javax.servlet.ServletException;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||||
|
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.validation.BindException;
|
import org.springframework.validation.BindException;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.MapBindingResult;
|
import org.springframework.validation.MapBindingResult;
|
||||||
@@ -54,21 +59,24 @@ class DefaultErrorAttributesTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void includeTimeStamp() {
|
void includeTimeStamp() {
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("timestamp")).isInstanceOf(Date.class);
|
assertThat(attributes.get("timestamp")).isInstanceOf(Date.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void specificStatusCode() {
|
void specificStatusCode() {
|
||||||
this.request.setAttribute("javax.servlet.error.status_code", 404);
|
this.request.setAttribute("javax.servlet.error.status_code", 404);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("error")).isEqualTo(HttpStatus.NOT_FOUND.getReasonPhrase());
|
assertThat(attributes.get("error")).isEqualTo(HttpStatus.NOT_FOUND.getReasonPhrase());
|
||||||
assertThat(attributes.get("status")).isEqualTo(404);
|
assertThat(attributes.get("status")).isEqualTo(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void missingStatusCode() {
|
void missingStatusCode() {
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("error")).isEqualTo("None");
|
assertThat(attributes.get("error")).isEqualTo("None");
|
||||||
assertThat(attributes.get("status")).isEqualTo(999);
|
assertThat(attributes.get("status")).isEqualTo(999);
|
||||||
}
|
}
|
||||||
@@ -78,7 +86,8 @@ class DefaultErrorAttributesTests {
|
|||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
ModelAndView modelAndView = this.errorAttributes.resolveException(this.request, null, null, ex);
|
ModelAndView modelAndView = this.errorAttributes.resolveException(this.request, null, null, ex);
|
||||||
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException("Ignored"));
|
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException("Ignored"));
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
||||||
assertThat(modelAndView).isNull();
|
assertThat(modelAndView).isNull();
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
@@ -89,7 +98,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void servletErrorWithMessage() {
|
void servletErrorWithMessage() {
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
@@ -99,7 +109,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void servletErrorWithoutMessage() {
|
void servletErrorWithoutMessage() {
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message").toString()).contains("");
|
assertThat(attributes.get("message").toString()).contains("");
|
||||||
@@ -108,7 +119,8 @@ class DefaultErrorAttributesTests {
|
|||||||
@Test
|
@Test
|
||||||
void servletMessageWithMessage() {
|
void servletMessageWithMessage() {
|
||||||
this.request.setAttribute("javax.servlet.error.message", "Test");
|
this.request.setAttribute("javax.servlet.error.message", "Test");
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
@@ -116,7 +128,8 @@ class DefaultErrorAttributesTests {
|
|||||||
@Test
|
@Test
|
||||||
void servletMessageWithoutMessage() {
|
void servletMessageWithoutMessage() {
|
||||||
this.request.setAttribute("javax.servlet.error.message", "Test");
|
this.request.setAttribute("javax.servlet.error.message", "Test");
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).asString().contains("");
|
assertThat(attributes.get("message")).asString().contains("");
|
||||||
}
|
}
|
||||||
@@ -125,7 +138,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void nullExceptionMessage() {
|
void nullExceptionMessage() {
|
||||||
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException());
|
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException());
|
||||||
this.request.setAttribute("javax.servlet.error.message", "Test");
|
this.request.setAttribute("javax.servlet.error.message", "Test");
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
@@ -133,7 +147,8 @@ class DefaultErrorAttributesTests {
|
|||||||
@Test
|
@Test
|
||||||
void nullExceptionMessageAndServletMessage() {
|
void nullExceptionMessageAndServletMessage() {
|
||||||
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException());
|
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException());
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("No message available");
|
assertThat(attributes.get("message")).isEqualTo("No message available");
|
||||||
}
|
}
|
||||||
@@ -143,7 +158,8 @@ class DefaultErrorAttributesTests {
|
|||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
ServletException wrapped = new ServletException(new ServletException(ex));
|
ServletException wrapped = new ServletException(new ServletException(ex));
|
||||||
this.request.setAttribute("javax.servlet.error.exception", wrapped);
|
this.request.setAttribute("javax.servlet.error.exception", wrapped);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(wrapped);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(wrapped);
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
@@ -153,7 +169,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void getError() {
|
void getError() {
|
||||||
Error error = new OutOfMemoryError("Test error");
|
Error error = new OutOfMemoryError("Test error");
|
||||||
this.request.setAttribute("javax.servlet.error.exception", error);
|
this.request.setAttribute("javax.servlet.error.exception", error);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.MESSAGE));
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(error);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(error);
|
||||||
assertThat(attributes.containsKey("exception")).isFalse();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test error");
|
assertThat(attributes.get("message")).isEqualTo("Test error");
|
||||||
@@ -164,7 +181,7 @@ class DefaultErrorAttributesTests {
|
|||||||
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
||||||
bindingResult.addError(new ObjectError("c", "d"));
|
bindingResult.addError(new ObjectError("c", "d"));
|
||||||
Exception ex = new BindException(bindingResult);
|
Exception ex = new BindException(bindingResult);
|
||||||
testBindingResult(bindingResult, ex, true);
|
testBindingResult(bindingResult, ex, ErrorAttributeOptions.of(Include.MESSAGE, Include.BINDING_ERRORS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -172,38 +189,44 @@ class DefaultErrorAttributesTests {
|
|||||||
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
||||||
bindingResult.addError(new ObjectError("c", "d"));
|
bindingResult.addError(new ObjectError("c", "d"));
|
||||||
Exception ex = new BindException(bindingResult);
|
Exception ex = new BindException(bindingResult);
|
||||||
testBindingResult(bindingResult, ex, false);
|
testBindingResult(bindingResult, ex, ErrorAttributeOptions.defaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void withMethodArgumentNotValidExceptionBindingErrors() {
|
void withMethodArgumentNotValidExceptionBindingErrors() {
|
||||||
|
Method method = ReflectionUtils.findMethod(String.class, "substring", int.class);
|
||||||
|
MethodParameter parameter = new MethodParameter(method, 0);
|
||||||
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
||||||
bindingResult.addError(new ObjectError("c", "d"));
|
bindingResult.addError(new ObjectError("c", "d"));
|
||||||
Exception ex = new MethodArgumentNotValidException(null, bindingResult);
|
Exception ex = new MethodArgumentNotValidException(parameter, bindingResult);
|
||||||
testBindingResult(bindingResult, ex, true);
|
testBindingResult(bindingResult, ex, ErrorAttributeOptions.of(Include.MESSAGE, Include.BINDING_ERRORS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testBindingResult(BindingResult bindingResult, Exception ex, boolean includeMessageAndErrors) {
|
private void testBindingResult(BindingResult bindingResult, Exception ex, ErrorAttributeOptions options) {
|
||||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false,
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, options);
|
||||||
includeMessageAndErrors, includeMessageAndErrors);
|
if (options.isIncluded(Include.MESSAGE)) {
|
||||||
if (includeMessageAndErrors) {
|
|
||||||
assertThat(attributes.get("message"))
|
assertThat(attributes.get("message"))
|
||||||
.isEqualTo("Validation failed for object='objectName'. Error count: 1");
|
.isEqualTo("Validation failed for object='objectName'. Error count: 1");
|
||||||
assertThat(attributes.get("errors")).isEqualTo(bindingResult.getAllErrors());
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertThat(attributes.get("message")).isEqualTo("");
|
assertThat(attributes.get("message")).isEqualTo("");
|
||||||
|
}
|
||||||
|
if (options.isIncluded(Include.BINDING_ERRORS)) {
|
||||||
|
assertThat(attributes.get("errors")).isEqualTo(bindingResult.getAllErrors());
|
||||||
|
}
|
||||||
|
else {
|
||||||
assertThat(attributes.containsKey("errors")).isFalse();
|
assertThat(attributes.containsKey("errors")).isFalse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void withExceptionAttribute() {
|
void withExceptionAttribute() {
|
||||||
DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes(true);
|
DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes();
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||||
Map<String, Object> attributes = errorAttributes.getErrorAttributes(this.webRequest, false, true, false);
|
Map<String, Object> attributes = errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.EXCEPTION, Include.MESSAGE));
|
||||||
assertThat(attributes.get("exception")).isEqualTo(RuntimeException.class.getName());
|
assertThat(attributes.get("exception")).isEqualTo(RuntimeException.class.getName());
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
@@ -212,7 +235,8 @@ class DefaultErrorAttributesTests {
|
|||||||
void withStackTraceAttribute() {
|
void withStackTraceAttribute() {
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, true, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.of(Include.STACK_TRACE));
|
||||||
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,14 +244,16 @@ class DefaultErrorAttributesTests {
|
|||||||
void withoutStackTraceAttribute() {
|
void withoutStackTraceAttribute() {
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
this.request.setAttribute("javax.servlet.error.exception", ex);
|
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.containsKey("trace")).isFalse();
|
assertThat(attributes.containsKey("trace")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void path() {
|
void path() {
|
||||||
this.request.setAttribute("javax.servlet.error.request_uri", "path");
|
this.request.setAttribute("javax.servlet.error.request_uri", "path");
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest,
|
||||||
|
ErrorAttributeOptions.defaults());
|
||||||
assertThat(attributes.get("path")).isEqualTo("path");
|
assertThat(attributes.get("path")).isEqualTo("path");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,12 +22,18 @@
|
|||||||
<allow pkg="org.springframework.boot.web.embedded" />
|
<allow pkg="org.springframework.boot.web.embedded" />
|
||||||
<allow pkg="org.springframework.boot.web.servlet" />
|
<allow pkg="org.springframework.boot.web.servlet" />
|
||||||
<allow pkg="org.springframework.web.servlet" />
|
<allow pkg="org.springframework.web.servlet" />
|
||||||
|
<subpackage name="error">
|
||||||
|
<allow pkg="org.springframework.boot.web.error" />
|
||||||
|
</subpackage>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
<subpackage name="reactive">
|
<subpackage name="reactive">
|
||||||
<allow pkg="org.springframework.boot.web.codec" />
|
<allow pkg="org.springframework.boot.web.codec" />
|
||||||
<allow pkg="org.springframework.boot.web.embedded" />
|
<allow pkg="org.springframework.boot.web.embedded" />
|
||||||
<allow pkg="org.springframework.boot.web.reactive" />
|
<allow pkg="org.springframework.boot.web.reactive" />
|
||||||
<allow pkg="org.springframework.web.reactive" />
|
<allow pkg="org.springframework.web.reactive" />
|
||||||
|
<subpackage name="error">
|
||||||
|
<allow pkg="org.springframework.boot.web.error" />
|
||||||
|
</subpackage>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
@@ -99,6 +105,7 @@
|
|||||||
<allow pkg="org.springframework.web.servlet" />
|
<allow pkg="org.springframework.web.servlet" />
|
||||||
</subpackage>
|
</subpackage>
|
||||||
<subpackage name="error">
|
<subpackage name="error">
|
||||||
|
<allow pkg="org.springframework.boot.web.error" />
|
||||||
<allow pkg="org.springframework.web.servlet" />
|
<allow pkg="org.springframework.web.servlet" />
|
||||||
</subpackage>
|
</subpackage>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
@@ -110,6 +117,9 @@
|
|||||||
<allow pkg="org.springframework.boot.web.server" />
|
<allow pkg="org.springframework.boot.web.server" />
|
||||||
<allow pkg="org.springframework.boot.web.reactive.server" />
|
<allow pkg="org.springframework.boot.web.reactive.server" />
|
||||||
</subpackage>
|
</subpackage>
|
||||||
|
<subpackage name="error">
|
||||||
|
<allow pkg="org.springframework.boot.web.error" />
|
||||||
|
</subpackage>
|
||||||
<subpackage name="server">
|
<subpackage name="server">
|
||||||
<allow pkg="org.springframework.boot.web.server" />
|
<allow pkg="org.springframework.boot.web.server" />
|
||||||
<disallow pkg="org.springframework.context" />
|
<disallow pkg="org.springframework.context" />
|
||||||
|
|||||||
Reference in New Issue
Block a user