Disable exception details on default error views
Prior to this commit, default error responses included the message from a handled exception. When the exception was a BindException, the error responses could also include an errors attribute containing the details of the binding failure. These details could leak information about the application. This commit removes the exception message and binding errors detail from error responses by default, and introduces a `server.error.include-details` property that can be used to cause these details to be included in the response. Fixes gh-20505
This commit is contained in:
parent
866147405c
commit
70d4994502
|
@ -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.
|
||||||
|
@ -47,7 +47,7 @@ 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, false);
|
return this.errorAttributes.getErrorAttributes(request, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
* Integration tests for {@link WebMvcEndpointChildContextConfiguration}.
|
* Integration tests for {@link WebMvcEndpointChildContextConfiguration}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
class WebMvcEndpointChildContextConfigurationIntegrationTests {
|
class WebMvcEndpointChildContextConfigurationIntegrationTests {
|
||||||
|
|
||||||
|
@ -59,7 +60,8 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
|
||||||
WebClient client = WebClient.create("http://localhost:" + port);
|
WebClient client = WebClient.create("http://localhost:" + port);
|
||||||
ClientResponse response = client.get().uri("actuator/fail").accept(MediaType.APPLICATION_JSON)
|
ClientResponse response = client.get().uri("actuator/fail").accept(MediaType.APPLICATION_JSON)
|
||||||
.exchange().block();
|
.exchange().block();
|
||||||
assertThat(response.bodyToMono(String.class).block()).contains("message\":\"Epic Fail");
|
assertThat(response.bodyToMono(String.class).block())
|
||||||
|
.contains("message\":\"An error occurred while processing the 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.
|
||||||
|
@ -61,6 +61,7 @@ import static org.mockito.Mockito.verify;
|
||||||
*
|
*
|
||||||
* @param <T> the type of application context used by the tests
|
* @param <T> the type of application context used by the tests
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractWebEndpointIntegrationTests<T extends ConfigurableApplicationContext & AnnotationConfigRegistry> {
|
public abstract class AbstractWebEndpointIntegrationTests<T extends ConfigurableApplicationContext & AnnotationConfigRegistry> {
|
||||||
|
|
||||||
|
@ -409,8 +410,10 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
|
||||||
BiConsumer<ApplicationContext, WebTestClient> consumer) {
|
BiConsumer<ApplicationContext, WebTestClient> consumer) {
|
||||||
T applicationContext = this.applicationContextSupplier.get();
|
T applicationContext = this.applicationContextSupplier.get();
|
||||||
contextCustomizer.accept(applicationContext);
|
contextCustomizer.accept(applicationContext);
|
||||||
applicationContext.getEnvironment().getPropertySources()
|
Map<String, Object> properties = new HashMap<>();
|
||||||
.addLast(new MapPropertySource("test", Collections.singletonMap("endpointPath", endpointPath)));
|
properties.put("endpointPath", endpointPath);
|
||||||
|
properties.put("server.error.include-details", "always");
|
||||||
|
applicationContext.getEnvironment().getPropertySources().addLast(new MapPropertySource("test", properties));
|
||||||
applicationContext.refresh();
|
applicationContext.refresh();
|
||||||
try {
|
try {
|
||||||
InetSocketAddress address = new InetSocketAddress(getPort(applicationContext));
|
InetSocketAddress address = new InetSocketAddress(getPort(applicationContext));
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||||
* @author Michael Stummvoll
|
* @author Michael Stummvoll
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Vedran Pavic
|
* @author Vedran Pavic
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
*/
|
*/
|
||||||
public class ErrorProperties {
|
public class ErrorProperties {
|
||||||
|
@ -40,10 +41,15 @@ public class ErrorProperties {
|
||||||
private boolean includeException;
|
private boolean includeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When to include a "stacktrace" attribute.
|
* When to include the "trace" attribute.
|
||||||
*/
|
*/
|
||||||
private IncludeStacktrace includeStacktrace = IncludeStacktrace.NEVER;
|
private IncludeStacktrace includeStacktrace = IncludeStacktrace.NEVER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When to include "message" and "errors" attributes.
|
||||||
|
*/
|
||||||
|
private IncludeDetails includeDetails = IncludeDetails.NEVER;
|
||||||
|
|
||||||
private final Whitelabel whitelabel = new Whitelabel();
|
private final Whitelabel whitelabel = new Whitelabel();
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
|
@ -70,6 +76,14 @@ public class ErrorProperties {
|
||||||
this.includeStacktrace = includeStacktrace;
|
this.includeStacktrace = includeStacktrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IncludeDetails getIncludeDetails() {
|
||||||
|
return this.includeDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIncludeDetails(IncludeDetails includeDetails) {
|
||||||
|
this.includeDetails = includeDetails;
|
||||||
|
}
|
||||||
|
|
||||||
public Whitelabel getWhitelabel() {
|
public Whitelabel getWhitelabel() {
|
||||||
return this.whitelabel;
|
return this.whitelabel;
|
||||||
}
|
}
|
||||||
|
@ -96,6 +110,28 @@ public class ErrorProperties {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include error details attributes options.
|
||||||
|
*/
|
||||||
|
public enum IncludeDetails {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Never add error detail information.
|
||||||
|
*/
|
||||||
|
NEVER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always add error detail information.
|
||||||
|
*/
|
||||||
|
ALWAYS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add error details information when the "details" request parameter is "true".
|
||||||
|
*/
|
||||||
|
ON_DETAILS_PARAM
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static class Whitelabel {
|
public static class Whitelabel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -54,6 +54,7 @@ import org.springframework.web.util.HtmlUtils;
|
||||||
* Abstract base class for {@link ErrorWebExceptionHandler} implementations.
|
* Abstract base class for {@link ErrorWebExceptionHandler} implementations.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @see ErrorAttributes
|
* @see ErrorAttributes
|
||||||
*/
|
*/
|
||||||
|
@ -131,10 +132,26 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
||||||
* views or JSON payloads.
|
* views or JSON payloads.
|
||||||
* @param request the source request
|
* @param request the source request
|
||||||
* @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
|
||||||
|
* {@link #getErrorAttributes(ServerRequest, boolean, boolean)}
|
||||||
*/
|
*/
|
||||||
|
@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);
|
return this.errorAttributes.getErrorAttributes(request, includeStackTrace, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the error attributes from the current request, to be used to populate error
|
||||||
|
* views or JSON payloads.
|
||||||
|
* @param request the source request
|
||||||
|
* @param includeStackTrace whether to include the error stacktrace information
|
||||||
|
* @param includeDetails whether to include message and errors attributes
|
||||||
|
* @return the error attributes as a Map
|
||||||
|
*/
|
||||||
|
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace,
|
||||||
|
boolean includeDetails) {
|
||||||
|
return this.errorAttributes.getErrorAttributes(request, includeStackTrace, includeDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,7 +169,21 @@ public abstract class AbstractErrorWebExceptionHandler implements ErrorWebExcept
|
||||||
* @return {@code true} if the error trace has been requested, {@code false} otherwise
|
* @return {@code true} if the error trace has been requested, {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
protected boolean isTraceEnabled(ServerRequest request) {
|
protected boolean isTraceEnabled(ServerRequest request) {
|
||||||
String parameter = request.queryParam("trace").orElse("false");
|
return getBooleanParameter(request, "trace");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the details attribute has been set on the given request.
|
||||||
|
* @param request the source request
|
||||||
|
* @return {@code true} if the error details have been requested, {@code false}
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
protected boolean isDetailsEnabled(ServerRequest request) {
|
||||||
|
return getBooleanParameter(request, "details");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean getBooleanParameter(ServerRequest request, String parameterName) {
|
||||||
|
String parameter = request.queryParam(parameterName).orElse("false");
|
||||||
return !"false".equalsIgnoreCase(parameter);
|
return !"false".equalsIgnoreCase(parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -71,6 +71,7 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r
|
||||||
* payload.
|
* payload.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
|
public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
|
||||||
|
@ -113,7 +114,8 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
||||||
*/
|
*/
|
||||||
protected Mono<ServerResponse> renderErrorView(ServerRequest request) {
|
protected Mono<ServerResponse> renderErrorView(ServerRequest request) {
|
||||||
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.TEXT_HTML);
|
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.TEXT_HTML);
|
||||||
Map<String, Object> error = getErrorAttributes(request, includeStackTrace);
|
boolean includeDetails = isIncludeDetails(request, MediaType.TEXT_HTML);
|
||||||
|
Map<String, Object> error = getErrorAttributes(request, includeStackTrace, includeDetails);
|
||||||
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[] {}))
|
||||||
|
@ -141,7 +143,8 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
||||||
*/
|
*/
|
||||||
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
|
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
|
||||||
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
|
boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
|
||||||
Map<String, Object> error = getErrorAttributes(request, includeStackTrace);
|
boolean includeDetails = isIncludeDetails(request, MediaType.ALL);
|
||||||
|
Map<String, Object> error = getErrorAttributes(request, includeStackTrace, includeDetails);
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
@ -163,6 +166,23 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the message and errors attributes should be included.
|
||||||
|
* @param request the source request
|
||||||
|
* @param produces the media type produced (or {@code MediaType.ALL})
|
||||||
|
* @return if the message and errors attributes should be included
|
||||||
|
*/
|
||||||
|
protected boolean isIncludeDetails(ServerRequest request, MediaType produces) {
|
||||||
|
ErrorProperties.IncludeDetails include = this.errorProperties.getIncludeDetails();
|
||||||
|
if (include == ErrorProperties.IncludeDetails.ALWAYS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (include == ErrorProperties.IncludeDetails.ON_DETAILS_PARAM) {
|
||||||
|
return isDetailsEnabled(request);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the HTTP error status information from the error map.
|
* Get the HTTP error status information from the error map.
|
||||||
* @param errorAttributes the current error information
|
* @param errorAttributes the current error information
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -38,6 +38,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
* @see ErrorAttributes
|
* @see ErrorAttributes
|
||||||
*/
|
*/
|
||||||
|
@ -66,13 +67,35 @@ public abstract class AbstractErrorController implements ErrorController {
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Map} of the error attributes.
|
||||||
|
* @param request the source request
|
||||||
|
* @param includeStackTrace if stack trace elements should be included
|
||||||
|
* @return the error attributes
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #getErrorAttributes(HttpServletRequest, boolean, boolean)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
||||||
|
return this.getErrorAttributes(request, includeStackTrace, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace,
|
||||||
|
boolean includeDetails) {
|
||||||
WebRequest webRequest = new ServletWebRequest(request);
|
WebRequest webRequest = new ServletWebRequest(request);
|
||||||
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
|
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace, includeDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean getTraceParameter(HttpServletRequest request) {
|
protected boolean getTraceParameter(HttpServletRequest request) {
|
||||||
String parameter = request.getParameter("trace");
|
return getBooleanParameter(request, "trace");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean getDetailsParameter(HttpServletRequest request) {
|
||||||
|
return getBooleanParameter(request, "details");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean getBooleanParameter(HttpServletRequest request, String parameterName) {
|
||||||
|
String parameter = request.getParameter(parameterName);
|
||||||
if (parameter == null) {
|
if (parameter == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ 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.autoconfigure.web.ErrorProperties.IncludeDetails;
|
||||||
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
|
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
|
||||||
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;
|
||||||
|
@ -47,6 +48,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Michael Stummvoll
|
* @author Michael Stummvoll
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
* @see ErrorAttributes
|
* @see ErrorAttributes
|
||||||
* @see ErrorProperties
|
* @see ErrorProperties
|
||||||
|
@ -87,8 +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
|
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request,
|
||||||
.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
|
isIncludeStackTrace(request, MediaType.TEXT_HTML), isIncludeDetails(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);
|
||||||
|
@ -100,7 +102,8 @@ 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, isIncludeStackTrace(request, MediaType.ALL),
|
||||||
|
isIncludeDetails(request, MediaType.ALL));
|
||||||
return new ResponseEntity<>(body, status);
|
return new ResponseEntity<>(body, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +130,23 @@ public class BasicErrorController extends AbstractErrorController {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the error details attributes should be included.
|
||||||
|
* @param request the source request
|
||||||
|
* @param produces the media type produced (or {@code MediaType.ALL})
|
||||||
|
* @return if the error details attributes should be included
|
||||||
|
*/
|
||||||
|
protected boolean isIncludeDetails(HttpServletRequest request, MediaType produces) {
|
||||||
|
IncludeDetails include = getErrorProperties().getIncludeDetails();
|
||||||
|
if (include == IncludeDetails.ALWAYS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (include == IncludeDetails.ON_DETAILS_PARAM) {
|
||||||
|
return getDetailsParameter(request);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide access to the error properties.
|
* Provide access to the error properties.
|
||||||
* @return the error properties
|
* @return the error properties
|
||||||
|
|
|
@ -106,6 +106,10 @@
|
||||||
"description": "Minimum \"Content-Length\" value that is required for compression to be performed.",
|
"description": "Minimum \"Content-Length\" value that is required for compression to be performed.",
|
||||||
"defaultValue": "2KB"
|
"defaultValue": "2KB"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "server.error.include-details",
|
||||||
|
"defaultValue": "never"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "server.error.include-stacktrace",
|
"name": "server.error.include-stacktrace",
|
||||||
"defaultValue": "never"
|
"defaultValue": "never"
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -55,6 +55,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
* Integration tests for {@link DefaultErrorWebExceptionHandler}
|
* Integration tests for {@link DefaultErrorWebExceptionHandler}
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
@ExtendWith(OutputCaptureExtension.class)
|
@ExtendWith(OutputCaptureExtension.class)
|
||||||
class DefaultErrorWebExceptionHandlerIntegrationTests {
|
class DefaultErrorWebExceptionHandlerIntegrationTests {
|
||||||
|
@ -78,8 +79,8 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
||||||
client.get().uri("/").exchange().expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody()
|
client.get().uri("/").exchange().expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody()
|
||||||
.jsonPath("status").isEqualTo("500").jsonPath("error")
|
.jsonPath("status").isEqualTo("500").jsonPath("error")
|
||||||
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()).jsonPath("path").isEqualTo(("/"))
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()).jsonPath("path").isEqualTo(("/"))
|
||||||
.jsonPath("message").isEqualTo("Expected!").jsonPath("exception").doesNotExist().jsonPath("trace")
|
.jsonPath("message").isEmpty().jsonPath("exception").doesNotExist().jsonPath("trace").doesNotExist()
|
||||||
.doesNotExist().jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
.jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
||||||
assertThat(output).contains("500 Server Error for HTTP GET \"/\"")
|
assertThat(output).contains("500 Server Error for HTTP GET \"/\"")
|
||||||
.contains("java.lang.IllegalStateException: Expected!");
|
.contains("java.lang.IllegalStateException: Expected!");
|
||||||
});
|
});
|
||||||
|
@ -98,7 +99,7 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void htmlError() {
|
void htmlError() {
|
||||||
this.contextRunner.run((context) -> {
|
this.contextRunner.withPropertyValues("server.error.include-details=always").run((context) -> {
|
||||||
WebTestClient client = getWebClient(context);
|
WebTestClient client = getWebClient(context);
|
||||||
String body = client.get().uri("/").accept(MediaType.TEXT_HTML).exchange().expectStatus()
|
String body = client.get().uri("/").accept(MediaType.TEXT_HTML).exchange().expectStatus()
|
||||||
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectHeader().contentType(TEXT_HTML_UTF8)
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectHeader().contentType(TEXT_HTML_UTF8)
|
||||||
|
@ -114,6 +115,18 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
||||||
client.post().uri("/bind").contentType(MediaType.APPLICATION_JSON).bodyValue("{}").exchange().expectStatus()
|
client.post().uri("/bind").contentType(MediaType.APPLICATION_JSON).bodyValue("{}").exchange().expectStatus()
|
||||||
.isBadRequest().expectBody().jsonPath("status").isEqualTo("400").jsonPath("error")
|
.isBadRequest().expectBody().jsonPath("status").isEqualTo("400").jsonPath("error")
|
||||||
.isEqualTo(HttpStatus.BAD_REQUEST.getReasonPhrase()).jsonPath("path").isEqualTo(("/bind"))
|
.isEqualTo(HttpStatus.BAD_REQUEST.getReasonPhrase()).jsonPath("path").isEqualTo(("/bind"))
|
||||||
|
.jsonPath("exception").doesNotExist().jsonPath("errors").doesNotExist().jsonPath("message")
|
||||||
|
.isNotEmpty().jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void bindingResultErrorIncludeDetails() {
|
||||||
|
this.contextRunner.withPropertyValues("server.error.include-details=on-details-param").run((context) -> {
|
||||||
|
WebTestClient client = getWebClient(context);
|
||||||
|
client.post().uri("/bind?details=true").contentType(MediaType.APPLICATION_JSON).bodyValue("{}").exchange()
|
||||||
|
.expectStatus().isBadRequest().expectBody().jsonPath("status").isEqualTo("400").jsonPath("error")
|
||||||
|
.isEqualTo(HttpStatus.BAD_REQUEST.getReasonPhrase()).jsonPath("path").isEqualTo(("/bind"))
|
||||||
.jsonPath("exception").doesNotExist().jsonPath("errors").isArray().jsonPath("message").isNotEmpty()
|
.jsonPath("exception").doesNotExist().jsonPath("errors").isArray().jsonPath("message").isNotEmpty()
|
||||||
.jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
.jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
||||||
});
|
});
|
||||||
|
@ -163,6 +176,50 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void includeDetailsOnParam() {
|
||||||
|
this.contextRunner.withPropertyValues("server.error.include-exception=true",
|
||||||
|
"server.error.include-details=on-details-param").run((context) -> {
|
||||||
|
WebTestClient client = getWebClient(context);
|
||||||
|
client.get().uri("/?details=true").exchange().expectStatus()
|
||||||
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody().jsonPath("status")
|
||||||
|
.isEqualTo("500").jsonPath("error")
|
||||||
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()).jsonPath("exception")
|
||||||
|
.isEqualTo(IllegalStateException.class.getName()).jsonPath("message").isNotEmpty()
|
||||||
|
.jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void alwaysIncludeDetails() {
|
||||||
|
this.contextRunner
|
||||||
|
.withPropertyValues("server.error.include-exception=true", "server.error.include-details=always")
|
||||||
|
.run((context) -> {
|
||||||
|
WebTestClient client = getWebClient(context);
|
||||||
|
client.get().uri("/?trace=false").exchange().expectStatus()
|
||||||
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody().jsonPath("status")
|
||||||
|
.isEqualTo("500").jsonPath("error")
|
||||||
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()).jsonPath("exception")
|
||||||
|
.isEqualTo(IllegalStateException.class.getName()).jsonPath("message").isNotEmpty()
|
||||||
|
.jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void neverIncludeDetails() {
|
||||||
|
this.contextRunner
|
||||||
|
.withPropertyValues("server.error.include-exception=true", "server.error.include-details=never")
|
||||||
|
.run((context) -> {
|
||||||
|
WebTestClient client = getWebClient(context);
|
||||||
|
client.get().uri("/?trace=true").exchange().expectStatus()
|
||||||
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody().jsonPath("status")
|
||||||
|
.isEqualTo("500").jsonPath("error")
|
||||||
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()).jsonPath("exception")
|
||||||
|
.isEqualTo(IllegalStateException.class.getName()).jsonPath("message").isEmpty()
|
||||||
|
.jsonPath("requestId").isEqualTo(this.logIdFilter.getLogId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void statusException() {
|
void statusException() {
|
||||||
this.contextRunner.withPropertyValues("server.error.include-exception=true").run((context) -> {
|
this.contextRunner.withPropertyValues("server.error.include-exception=true").run((context) -> {
|
||||||
|
@ -176,8 +233,10 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void defaultErrorView() {
|
void defaultErrorView() {
|
||||||
this.contextRunner.withPropertyValues("spring.mustache.prefix=classpath:/unknown/",
|
this.contextRunner
|
||||||
"server.error.include-stacktrace=always").run((context) -> {
|
.withPropertyValues("spring.mustache.prefix=classpath:/unknown/",
|
||||||
|
"server.error.include-stacktrace=always", "server.error.include-details=always")
|
||||||
|
.run((context) -> {
|
||||||
WebTestClient client = getWebClient(context);
|
WebTestClient client = getWebClient(context);
|
||||||
String body = client.get().uri("/").accept(MediaType.TEXT_HTML).exchange().expectStatus()
|
String body = client.get().uri("/").accept(MediaType.TEXT_HTML).exchange().expectStatus()
|
||||||
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectHeader().contentType(TEXT_HTML_UTF8)
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectHeader().contentType(TEXT_HTML_UTF8)
|
||||||
|
@ -190,7 +249,9 @@ class DefaultErrorWebExceptionHandlerIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void escapeHtmlInDefaultErrorView() {
|
void escapeHtmlInDefaultErrorView() {
|
||||||
this.contextRunner.withPropertyValues("spring.mustache.prefix=classpath:/unknown/").run((context) -> {
|
this.contextRunner
|
||||||
|
.withPropertyValues("spring.mustache.prefix=classpath:/unknown/", "server.error.include-details=always")
|
||||||
|
.run((context) -> {
|
||||||
WebTestClient client = getWebClient(context);
|
WebTestClient client = getWebClient(context);
|
||||||
String body = client.get().uri("/html").accept(MediaType.TEXT_HTML).exchange().expectStatus()
|
String body = client.get().uri("/html").accept(MediaType.TEXT_HTML).exchange().expectStatus()
|
||||||
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectHeader().contentType(TEXT_HTML_UTF8)
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectHeader().contentType(TEXT_HTML_UTF8)
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -47,6 +47,7 @@ import static org.mockito.Mockito.mock;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
class DefaultErrorWebExceptionHandlerTests {
|
class DefaultErrorWebExceptionHandlerTests {
|
||||||
|
|
||||||
|
@ -65,7 +66,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())).willReturn(getErrorAttributes());
|
given(errorAttributes.getErrorAttributes(any(), anyBoolean(), anyBoolean())).willReturn(getErrorAttributes());
|
||||||
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
|
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
|
||||||
resourceProperties, errorProperties, context);
|
resourceProperties, errorProperties, context);
|
||||||
setupViewResolver(exceptionHandler);
|
setupViewResolver(exceptionHandler);
|
||||||
|
|
|
@ -70,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
class BasicErrorControllerIntegrationTests {
|
class BasicErrorControllerIntegrationTests {
|
||||||
|
|
||||||
|
@ -84,59 +85,87 @@ class BasicErrorControllerIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
void testErrorForMachineClient() {
|
void testErrorForMachineClientDefault() {
|
||||||
load();
|
load();
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("?trace=true"), Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("?trace=true"), Map.class);
|
||||||
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", null, "Expected!", "/");
|
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", null,
|
||||||
|
"An error occurred while processing the request", "/");
|
||||||
|
assertThat(entity.getBody().containsKey("exception")).isFalse();
|
||||||
assertThat(entity.getBody().containsKey("trace")).isFalse();
|
assertThat(entity.getBody().containsKey("trace")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testErrorForMachineClientTraceParamTrue() {
|
void testErrorForMachineClientWithParamsTrue() {
|
||||||
errorForMachineClientOnTraceParam("?trace=true", true);
|
load("--server.error.include-exception=true", "--server.error.include-stacktrace=on-trace-param",
|
||||||
|
"--server.error.include-details=on-details-param");
|
||||||
|
exceptionWithStackTraceAndDetails("?trace=true&details=true");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testErrorForMachineClientTraceParamFalse() {
|
void testErrorForMachineClientWithParamsFalse() {
|
||||||
errorForMachineClientOnTraceParam("?trace=false", false);
|
load("--server.error.include-exception=true", "--server.error.include-stacktrace=on-trace-param",
|
||||||
|
"--server.error.include-details=on-details-param");
|
||||||
|
exceptionWithoutStackTraceAndDetails("?trace=false&details=false");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testErrorForMachineClientTraceParamAbsent() {
|
void testErrorForMachineClientWithParamsAbsent() {
|
||||||
errorForMachineClientOnTraceParam("", false);
|
load("--server.error.include-exception=true", "--server.error.include-stacktrace=on-trace-param",
|
||||||
|
"--server.error.include-details=on-details-param");
|
||||||
|
exceptionWithoutStackTraceAndDetails("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testErrorForMachineClientNeverParams() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-stacktrace=never",
|
||||||
|
"--server.error.include-details=never");
|
||||||
|
exceptionWithoutStackTraceAndDetails("?trace=true&details=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testErrorForMachineClientAlwaysParams() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-stacktrace=always",
|
||||||
|
"--server.error.include-details=always");
|
||||||
|
exceptionWithStackTraceAndDetails("?trace=false&details=false");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testErrorForMachineClientAlwaysParamsWithoutMessage() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-details=always");
|
||||||
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/noMessage"), Map.class);
|
||||||
|
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", IllegalStateException.class,
|
||||||
|
"No message available", "/noMessage");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
private void errorForMachineClientOnTraceParam(String path, boolean expectedTrace) {
|
private void exceptionWithStackTraceAndDetails(String path) {
|
||||||
load("--server.error.include-exception=true", "--server.error.include-stacktrace=on-trace-param");
|
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl(path), Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl(path), Map.class);
|
||||||
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", IllegalStateException.class,
|
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", IllegalStateException.class,
|
||||||
"Expected!", "/");
|
"Expected!", "/");
|
||||||
assertThat(entity.getBody().containsKey("trace")).isEqualTo(expectedTrace);
|
assertThat(entity.getBody().containsKey("trace")).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
void testErrorForMachineClientNoStacktrace() {
|
private void exceptionWithoutStackTraceAndDetails(String path) {
|
||||||
load("--server.error.include-stacktrace=never");
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl(path), Map.class);
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("?trace=true"), Map.class);
|
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", IllegalStateException.class,
|
||||||
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", null, "Expected!", "/");
|
"An error occurred while processing the request", "/");
|
||||||
assertThat(entity.getBody().containsKey("trace")).isFalse();
|
assertThat(entity.getBody().containsKey("trace")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
void testErrorForMachineClientAlwaysStacktrace() {
|
void testErrorForAnnotatedExceptionWithoutDetails() {
|
||||||
load("--server.error.include-stacktrace=always");
|
load("--server.error.include-exception=true");
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("?trace=false"), Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotated"), Map.class);
|
||||||
assertErrorAttributes(entity.getBody(), "500", "Internal Server Error", null, "Expected!", "/");
|
assertErrorAttributes(entity.getBody(), "400", "Bad Request", TestConfiguration.Errors.ExpectedException.class,
|
||||||
assertThat(entity.getBody().containsKey("trace")).isTrue();
|
"An error occurred while processing the request", "/annotated");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
void testErrorForAnnotatedException() {
|
void testErrorForAnnotatedExceptionWithDetails() {
|
||||||
load("--server.error.include-exception=true");
|
load("--server.error.include-exception=true", "--server.error.include-details=always");
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotated"), Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotated"), Map.class);
|
||||||
assertErrorAttributes(entity.getBody(), "400", "Bad Request", TestConfiguration.Errors.ExpectedException.class,
|
assertErrorAttributes(entity.getBody(), "400", "Bad Request", TestConfiguration.Errors.ExpectedException.class,
|
||||||
"Expected!", "/annotated");
|
"Expected!", "/annotated");
|
||||||
|
@ -144,25 +173,77 @@ class BasicErrorControllerIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
void testErrorForAnnotatedNoReasonException() {
|
void testErrorForAnnotatedNoReasonExceptionWithoutDetails() {
|
||||||
load("--server.error.include-exception=true");
|
load("--server.error.include-exception=true");
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotatedNoReason"), Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotatedNoReason"), Map.class);
|
||||||
|
assertErrorAttributes(entity.getBody(), "406", "Not Acceptable",
|
||||||
|
TestConfiguration.Errors.NoReasonExpectedException.class,
|
||||||
|
"An error occurred while processing the request", "/annotatedNoReason");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
void testErrorForAnnotatedNoReasonExceptionWithDetails() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-details=always");
|
||||||
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotatedNoReason"), Map.class);
|
||||||
assertErrorAttributes(entity.getBody(), "406", "Not Acceptable",
|
assertErrorAttributes(entity.getBody(), "406", "Not Acceptable",
|
||||||
TestConfiguration.Errors.NoReasonExpectedException.class, "Expected message", "/annotatedNoReason");
|
TestConfiguration.Errors.NoReasonExpectedException.class, "Expected message", "/annotatedNoReason");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
void testBindingExceptionForMachineClient() {
|
void testErrorForAnnotatedNoMessageExceptionWithDetails() {
|
||||||
load("--server.error.include-exception=true");
|
load("--server.error.include-exception=true", "--server.error.include-details=always");
|
||||||
RequestEntity request = RequestEntity.get(URI.create(createUrl("/bind"))).accept(MediaType.APPLICATION_JSON)
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/annotatedNoMessage"), Map.class);
|
||||||
.build();
|
assertErrorAttributes(entity.getBody(), "406", "Not Acceptable",
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
TestConfiguration.Errors.NoReasonExpectedException.class, "No message available",
|
||||||
String resp = entity.getBody().toString();
|
"/annotatedNoMessage");
|
||||||
assertThat(resp).contains("Error count: 1");
|
}
|
||||||
assertThat(resp).contains("errors=[{");
|
|
||||||
assertThat(resp).contains("codes=[");
|
@Test
|
||||||
assertThat(resp).contains("org.springframework.validation.BindException");
|
void testBindingExceptionForMachineClientWithDetailsParamTrue() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-details=on-details-param");
|
||||||
|
bindingExceptionWithDetails("?details=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testBindingExceptionForMachineClientWithDetailsParamFalse() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-details=on-details-param");
|
||||||
|
bindingExceptionWithoutDetails("?details=false");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testBindingExceptionForMachineClientWithDetailsParamAbsent() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-details=on-details-param");
|
||||||
|
bindingExceptionWithoutDetails("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testBindingExceptionForMachineClientAlwaysDetails() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-details=always");
|
||||||
|
bindingExceptionWithDetails("?details=false");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testBindingExceptionForMachineClientNeverDetails() {
|
||||||
|
load("--server.error.include-exception=true", "--server.error.include-details=never");
|
||||||
|
bindingExceptionWithoutDetails("?details=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
private void bindingExceptionWithDetails(String param) {
|
||||||
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/bind" + param), Map.class);
|
||||||
|
assertErrorAttributes(entity.getBody(), "400", "Bad Request", BindException.class,
|
||||||
|
"Validation failed for object='test'. Error count: 1", "/bind");
|
||||||
|
assertThat(entity.getBody().containsKey("errors")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
private void bindingExceptionWithoutDetails(String param) {
|
||||||
|
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(createUrl("/bind" + param), Map.class);
|
||||||
|
assertErrorAttributes(entity.getBody(), "400", "Bad Request", BindException.class, "Validation failed",
|
||||||
|
"/bind");
|
||||||
|
assertThat(entity.getBody().containsKey("errors")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -172,22 +253,21 @@ class BasicErrorControllerIntegrationTests {
|
||||||
RequestEntity request = RequestEntity.post(URI.create(createUrl("/bodyValidation")))
|
RequestEntity request = RequestEntity.post(URI.create(createUrl("/bodyValidation")))
|
||||||
.accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).body("{}");
|
.accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).body("{}");
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
||||||
String resp = entity.getBody().toString();
|
assertErrorAttributes(entity.getBody(), "400", "Bad Request", MethodArgumentNotValidException.class,
|
||||||
assertThat(resp).contains("Error count: 1");
|
"Validation failed", "/bodyValidation");
|
||||||
assertThat(resp).contains("errors=[{");
|
assertThat(entity.getBody().containsKey("errors")).isFalse();
|
||||||
assertThat(resp).contains("codes=[");
|
|
||||||
assertThat(resp).contains(MethodArgumentNotValidException.class.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
void testNoExceptionByDefaultForMachineClient() {
|
void testBindingExceptionForMachineClientDefault() {
|
||||||
load();
|
load();
|
||||||
RequestEntity request = RequestEntity.get(URI.create(createUrl("/bind"))).accept(MediaType.APPLICATION_JSON)
|
RequestEntity request = RequestEntity.get(URI.create(createUrl("/bind?trace=true,details=true")))
|
||||||
.build();
|
.accept(MediaType.APPLICATION_JSON).build();
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
||||||
String resp = entity.getBody().toString();
|
assertThat(entity.getBody().containsKey("exception")).isFalse();
|
||||||
assertThat(resp).doesNotContain("org.springframework.validation.BindException");
|
assertThat(entity.getBody().containsKey("trace")).isFalse();
|
||||||
|
assertThat(entity.getBody().containsKey("errors")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -213,7 +293,7 @@ class BasicErrorControllerIntegrationTests {
|
||||||
|
|
||||||
private void assertErrorAttributes(Map<?, ?> content, String status, String error, Class<?> exception,
|
private void assertErrorAttributes(Map<?, ?> content, String status, String error, Class<?> exception,
|
||||||
String message, String path) {
|
String message, String path) {
|
||||||
assertThat(content.get("status")).as("Wrong status").isEqualTo(status);
|
assertThat(content.get("status").toString()).as("Wrong status").isEqualTo(status);
|
||||||
assertThat(content.get("error")).as("Wrong error").isEqualTo(error);
|
assertThat(content.get("error")).as("Wrong error").isEqualTo(error);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
assertThat(content.get("exception")).as("Wrong exception").isEqualTo(exception.getName());
|
assertThat(content.get("exception")).as("Wrong exception").isEqualTo(exception.getName());
|
||||||
|
@ -282,6 +362,11 @@ class BasicErrorControllerIntegrationTests {
|
||||||
throw new IllegalStateException("Expected!");
|
throw new IllegalStateException("Expected!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/noMessage")
|
||||||
|
String noMessage() {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping("/annotated")
|
@RequestMapping("/annotated")
|
||||||
String annotated() {
|
String annotated() {
|
||||||
throw new ExpectedException();
|
throw new ExpectedException();
|
||||||
|
@ -292,6 +377,11 @@ class BasicErrorControllerIntegrationTests {
|
||||||
throw new NoReasonExpectedException("Expected message");
|
throw new NoReasonExpectedException("Expected message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/annotatedNoMessage")
|
||||||
|
String annotatedNoMessage() {
|
||||||
|
throw new NoReasonExpectedException("");
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping("/bind")
|
@RequestMapping("/bind")
|
||||||
String bind() throws Exception {
|
String bind() throws Exception {
|
||||||
BindException error = new BindException(this, "test");
|
BindException error = new BindException(this, "test");
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -67,8 +67,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
* {@link SpringBootTest @SpringBootTest}.
|
* {@link SpringBootTest @SpringBootTest}.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
@SpringBootTest
|
@SpringBootTest(properties = { "server.error.include-details=always" })
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
class BasicErrorControllerMockMvcTests {
|
class BasicErrorControllerMockMvcTests {
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ class BasicErrorControllerMockMvcTests {
|
||||||
// And the rendered status code is always wrong (but would be 400 in a real
|
// And the rendered status code is always wrong (but would be 400 in a real
|
||||||
// system)
|
// system)
|
||||||
String content = response.getResponse().getContentAsString();
|
String content = response.getResponse().getContentAsString();
|
||||||
assertThat(content).contains("Error count: 1");
|
assertThat(content).contains("Validation failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -50,8 +50,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
* Integration tests for the default error view.
|
* Integration tests for the default error view.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
@SpringBootTest
|
@SpringBootTest(properties = { "server.error.include-details=always" })
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
class DefaultErrorViewIntegrationTests {
|
class DefaultErrorViewIntegrationTests {
|
||||||
|
|
||||||
|
|
|
@ -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,13 +45,13 @@ class ErrorMvcAutoConfigurationTests {
|
||||||
AutoConfigurations.of(DispatcherServletAutoConfiguration.class, ErrorMvcAutoConfiguration.class));
|
AutoConfigurations.of(DispatcherServletAutoConfiguration.class, ErrorMvcAutoConfiguration.class));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void renderContainsViewWithExceptionDetails() throws Exception {
|
void renderContainsViewWithExceptionDetails() {
|
||||||
this.contextRunner.run((context) -> {
|
this.contextRunner.run((context) -> {
|
||||||
View errorView = context.getBean("error", View.class);
|
View errorView = context.getBean("error", View.class);
|
||||||
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), webRequest.getRequest(),
|
errorView.render(errorAttributes.getErrorAttributes(webRequest, true, true), 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 +69,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), webRequest.getRequest(),
|
errorView.render(errorAttributes.getErrorAttributes(webRequest, true, true), 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 "
|
||||||
|
|
|
@ -71,6 +71,7 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro
|
||||||
properties.put("spring.resources.chain.cache", "false");
|
properties.put("spring.resources.chain.cache", "false");
|
||||||
properties.put("spring.template.provider.cache", "false");
|
properties.put("spring.template.provider.cache", "false");
|
||||||
properties.put("spring.mvc.log-resolved-exception", "true");
|
properties.put("spring.mvc.log-resolved-exception", "true");
|
||||||
|
properties.put("server.error.include-details", "ALWAYS");
|
||||||
properties.put("server.error.include-stacktrace", "ALWAYS");
|
properties.put("server.error.include-stacktrace", "ALWAYS");
|
||||||
properties.put("server.servlet.jsp.init-parameters.development", "true");
|
properties.put("server.servlet.jsp.init-parameters.development", "true");
|
||||||
properties.put("spring.reactor.debug", "true");
|
properties.put("spring.reactor.debug", "true");
|
||||||
|
|
|
@ -106,8 +106,10 @@ class DevToolPropertiesIntegrationTests {
|
||||||
application.setWebApplicationType(WebApplicationType.NONE);
|
application.setWebApplicationType(WebApplicationType.NONE);
|
||||||
this.context = getContext(application::run);
|
this.context = getContext(application::run);
|
||||||
ConfigurableEnvironment environment = this.context.getEnvironment();
|
ConfigurableEnvironment environment = this.context.getEnvironment();
|
||||||
String property = environment.getProperty("server.error.include-stacktrace");
|
String includeStackTrace = environment.getProperty("server.error.include-stacktrace");
|
||||||
assertThat(property).isEqualTo(ErrorProperties.IncludeStacktrace.ALWAYS.toString());
|
assertThat(includeStackTrace).isEqualTo(ErrorProperties.IncludeStacktrace.ALWAYS.toString());
|
||||||
|
String includeDetails = environment.getProperty("server.error.include-details");
|
||||||
|
assertThat(includeDetails).isEqualTo(ErrorProperties.IncludeDetails.ALWAYS.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ConfigurableApplicationContext getContext(Supplier<ConfigurableApplicationContext> supplier)
|
protected ConfigurableApplicationContext getContext(Supplier<ConfigurableApplicationContext> supplier)
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Michele Mancioppi
|
* @author Michele Mancioppi
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @see ErrorAttributes
|
* @see ErrorAttributes
|
||||||
*/
|
*/
|
||||||
|
@ -79,7 +80,14 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||||
|
return this.getErrorAttributes(request, includeStackTrace, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace,
|
||||||
|
boolean includeDetails) {
|
||||||
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());
|
||||||
|
@ -89,9 +97,9 @@ 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));
|
errorAttributes.put("message", determineMessage(error, responseStatusAnnotation, includeDetails));
|
||||||
errorAttributes.put("requestId", request.exchange().getRequest().getId());
|
errorAttributes.put("requestId", request.exchange().getRequest().getId());
|
||||||
handleException(errorAttributes, determineException(error), includeStackTrace);
|
handleException(errorAttributes, determineException(error), includeStackTrace, includeDetails);
|
||||||
return errorAttributes;
|
return errorAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,9 +110,13 @@ 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 includeDetails) {
|
||||||
if (error instanceof WebExchangeBindException) {
|
if (error instanceof WebExchangeBindException) {
|
||||||
return error.getMessage();
|
return includeDetails ? error.getMessage() : "Validation failed";
|
||||||
|
}
|
||||||
|
if (!includeDetails) {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
if (error instanceof ResponseStatusException) {
|
if (error instanceof ResponseStatusException) {
|
||||||
return ((ResponseStatusException) error).getReason();
|
return ((ResponseStatusException) error).getReason();
|
||||||
|
@ -130,14 +142,15 @@ 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 includeStackTrace,
|
||||||
|
boolean includeDetails) {
|
||||||
if (this.includeException) {
|
if (this.includeException) {
|
||||||
errorAttributes.put("exception", error.getClass().getName());
|
errorAttributes.put("exception", error.getClass().getName());
|
||||||
}
|
}
|
||||||
if (includeStackTrace) {
|
if (includeStackTrace) {
|
||||||
addStackTrace(errorAttributes, error);
|
addStackTrace(errorAttributes, error);
|
||||||
}
|
}
|
||||||
if (error instanceof BindingResult) {
|
if (includeDetails && (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());
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -26,6 +26,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||||
* Provides access to error attributes which can be logged or presented to the user.
|
* Provides access to error attributes which can be logged or presented to the user.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @see DefaultErrorAttributes
|
* @see DefaultErrorAttributes
|
||||||
*/
|
*/
|
||||||
|
@ -37,9 +38,21 @@ public interface ErrorAttributes {
|
||||||
* @param request the source request
|
* @param request the source request
|
||||||
* @param includeStackTrace if stack trace elements should be included
|
* @param includeStackTrace if stack trace elements should be included
|
||||||
* @return a map of error attributes
|
* @return a map of error attributes
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #getErrorAttributes(ServerRequest, boolean, boolean)}
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace);
|
Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @param request the source request
|
||||||
|
* @param includeStackTrace if stack trace elements should be included
|
||||||
|
* @param includeDetails if message and errors elements should be included
|
||||||
|
* @return a map of error attributes
|
||||||
|
*/
|
||||||
|
Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace, boolean includeDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* extracted.
|
* extracted.
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -46,9 +46,10 @@ import org.springframework.web.servlet.ModelAndView;
|
||||||
* <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>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
@ -56,6 +57,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Vedran Pavic
|
* @author Vedran Pavic
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @see ErrorAttributes
|
* @see ErrorAttributes
|
||||||
*/
|
*/
|
||||||
|
@ -99,11 +101,18 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||||
|
return this.getErrorAttributes(webRequest, includeStackTrace, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace,
|
||||||
|
boolean includeDetails) {
|
||||||
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);
|
addErrorDetails(errorAttributes, webRequest, includeStackTrace, includeDetails);
|
||||||
addPath(errorAttributes, webRequest);
|
addPath(errorAttributes, webRequest);
|
||||||
return errorAttributes;
|
return errorAttributes;
|
||||||
}
|
}
|
||||||
|
@ -125,8 +134,8 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest,
|
private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest, boolean includeStackTrace,
|
||||||
boolean includeStackTrace) {
|
boolean includeDetails) {
|
||||||
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) {
|
||||||
|
@ -135,32 +144,51 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
||||||
if (this.includeException) {
|
if (this.includeException) {
|
||||||
errorAttributes.put("exception", error.getClass().getName());
|
errorAttributes.put("exception", error.getClass().getName());
|
||||||
}
|
}
|
||||||
addErrorMessage(errorAttributes, error);
|
|
||||||
if (includeStackTrace) {
|
if (includeStackTrace) {
|
||||||
addStackTrace(errorAttributes, error);
|
addStackTrace(errorAttributes, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object message = getAttribute(webRequest, "javax.servlet.error.message");
|
addErrorMessage(errorAttributes, webRequest, error, includeDetails);
|
||||||
if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null)
|
}
|
||||||
&& !(error instanceof BindingResult)) {
|
|
||||||
errorAttributes.put("message", StringUtils.isEmpty(message) ? "No message available" : message);
|
private void addErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error,
|
||||||
|
boolean includeDetails) {
|
||||||
|
BindingResult result = extractBindingResult(error);
|
||||||
|
if (result == null) {
|
||||||
|
addExceptionErrorMessage(errorAttributes, webRequest, error, includeDetails);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addBindingResultErrorMessage(errorAttributes, result, includeDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
|
private void addExceptionErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error,
|
||||||
BindingResult result = extractBindingResult(error);
|
boolean includeDetails) {
|
||||||
if (result == null) {
|
if (!includeDetails) {
|
||||||
errorAttributes.put("message", error.getMessage());
|
errorAttributes.put("message", "An error occurred while processing the request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object message = getAttribute(webRequest, "javax.servlet.error.message");
|
||||||
|
if (StringUtils.isEmpty(message) && error != null) {
|
||||||
|
message = error.getMessage();
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(message)) {
|
||||||
|
message = "No message available";
|
||||||
|
}
|
||||||
|
errorAttributes.put("message", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBindingResultErrorMessage(Map<String, Object> errorAttributes, BindingResult result,
|
||||||
|
boolean includeDetails) {
|
||||||
|
if (!includeDetails) {
|
||||||
|
errorAttributes.put("message", "Validation failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
errorAttributes.put("errors", result.getAllErrors());
|
errorAttributes.put("errors", result.getAllErrors());
|
||||||
errorAttributes.put("message", "Validation failed for object='" + result.getObjectName()
|
|
||||||
+ "'. Error count: " + result.getErrorCount());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
errorAttributes.put("message", "No errors");
|
|
||||||
}
|
}
|
||||||
|
errorAttributes.put("message", "Validation failed for object='" + result.getObjectName() + "'. "
|
||||||
|
+ "Error count: " + result.getErrorCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
private BindingResult extractBindingResult(Throwable error) {
|
private BindingResult extractBindingResult(Throwable error) {
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -26,6 +26,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||||
* Provides access to error attributes which can be logged or presented to the user.
|
* Provides access to error attributes which can be logged or presented to the user.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @see DefaultErrorAttributes
|
* @see DefaultErrorAttributes
|
||||||
*/
|
*/
|
||||||
|
@ -38,9 +39,23 @@ public interface ErrorAttributes {
|
||||||
* @param webRequest the source request
|
* @param webRequest the source request
|
||||||
* @param includeStackTrace if stack trace elements should be included
|
* @param includeStackTrace if stack trace elements should be included
|
||||||
* @return a map of error attributes
|
* @return a map of error attributes
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #getErrorAttributes(WebRequest, boolean, boolean)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace);
|
Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* {@link ResponseBody @ResponseBody}.
|
||||||
|
* @param webRequest the source request
|
||||||
|
* @param includeStackTrace if stack trace elements should be included
|
||||||
|
* @param includeDetails if message and errors elements should be included
|
||||||
|
* @return a map of error attributes
|
||||||
|
*/
|
||||||
|
Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace, boolean includeDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* extracted.
|
* extracted.
|
||||||
|
|
|
@ -47,6 +47,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
class DefaultErrorAttributesTests {
|
class DefaultErrorAttributesTests {
|
||||||
|
|
||||||
|
@ -60,7 +61,8 @@ class DefaultErrorAttributesTests {
|
||||||
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().isThrownBy(() -> this.errorAttributes.getErrorAttributes(request, false))
|
assertThatIllegalStateException()
|
||||||
|
.isThrownBy(() -> this.errorAttributes.getErrorAttributes(request, false, false))
|
||||||
.withMessageContaining("Missing exception attribute in ServerWebExchange");
|
.withMessageContaining("Missing exception attribute in ServerWebExchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +70,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);
|
||||||
assertThat(attributes.get("timestamp")).isInstanceOf(Date.class);
|
assertThat(attributes.get("timestamp")).isInstanceOf(Date.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +79,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);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,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);
|
||||||
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());
|
||||||
|
@ -98,7 +100,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);
|
false, true);
|
||||||
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());
|
||||||
|
@ -109,7 +111,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);
|
false, true);
|
||||||
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!");
|
||||||
|
@ -119,7 +121,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);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -129,19 +131,29 @@ 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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true);
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void excludeDetails() {
|
||||||
|
Error error = new OutOfMemoryError("Test error");
|
||||||
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
|
ServerRequest serverRequest = buildServerRequest(request, error);
|
||||||
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, false);
|
||||||
|
assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error);
|
||||||
|
assertThat(attributes.get("message")).isEqualTo("");
|
||||||
|
}
|
||||||
|
|
||||||
@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(true);
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true);
|
||||||
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");
|
||||||
|
@ -154,7 +166,7 @@ class DefaultErrorAttributesTests {
|
||||||
this.errorAttributes = new DefaultErrorAttributes(true);
|
this.errorAttributes = new DefaultErrorAttributes(true);
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true);
|
||||||
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());
|
||||||
|
@ -168,7 +180,7 @@ class DefaultErrorAttributesTests {
|
||||||
this.errorAttributes = new DefaultErrorAttributes(true);
|
this.errorAttributes = new DefaultErrorAttributes(true);
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, true);
|
||||||
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());
|
||||||
|
@ -179,7 +191,7 @@ 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),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex), false,
|
||||||
false);
|
false);
|
||||||
assertThat(attributes.get("trace")).isNull();
|
assertThat(attributes.get("trace")).isNull();
|
||||||
}
|
}
|
||||||
|
@ -188,7 +200,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), true,
|
||||||
|
false);
|
||||||
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +209,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);
|
||||||
assertThat(attributes.get("path")).isEqualTo("/test");
|
assertThat(attributes.get("path")).isEqualTo("/test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +217,7 @@ 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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(serverRequest, false, false);
|
||||||
assertThat(attributes.get("requestId")).isEqualTo(serverRequest.exchange().getRequest().getId());
|
assertThat(attributes.get("requestId")).isEqualTo(serverRequest.exchange().getRequest().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,8 +229,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),
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex), false,
|
||||||
false);
|
true);
|
||||||
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"
|
||||||
|
@ -225,6 +238,20 @@ class DefaultErrorAttributesTests {
|
||||||
assertThat(attributes.get("errors")).isEqualTo(bindingResult.getAllErrors());
|
assertThat(attributes.get("errors")).isEqualTo(bindingResult.getAllErrors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void extractBindingResultErrorsExcludeDetails() throws Exception {
|
||||||
|
Method method = getClass().getDeclaredMethod("method", String.class);
|
||||||
|
MethodParameter stringParam = new MethodParameter(method, 0);
|
||||||
|
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
||||||
|
bindingResult.addError(new ObjectError("c", "d"));
|
||||||
|
Exception ex = new WebExchangeBindException(stringParam, bindingResult);
|
||||||
|
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
|
||||||
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, ex), false,
|
||||||
|
false);
|
||||||
|
assertThat(attributes.get("message")).isEqualTo("Validation failed");
|
||||||
|
assertThat(attributes.containsKey("errors")).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
private ServerRequest buildServerRequest(MockServerHttpRequest request, Throwable error) {
|
private ServerRequest buildServerRequest(MockServerHttpRequest request, Throwable error) {
|
||||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||||
this.errorAttributes.storeErrorInformation(error, exchange);
|
this.errorAttributes.storeErrorInformation(error, exchange);
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -42,32 +42,33 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Vedran Pavic
|
* @author Vedran Pavic
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
class DefaultErrorAttributesTests {
|
class DefaultErrorAttributesTests {
|
||||||
|
|
||||||
private DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes();
|
private final DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes();
|
||||||
|
|
||||||
private MockHttpServletRequest request = new MockHttpServletRequest();
|
private final MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
|
||||||
private WebRequest webRequest = new ServletWebRequest(this.request);
|
private final WebRequest webRequest = new ServletWebRequest(this.request);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void includeTimeStamp() {
|
void includeTimeStamp() {
|
||||||
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false);
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false);
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false);
|
||||||
assertThat(attributes.get("error")).isEqualTo("None");
|
assertThat(attributes.get("error")).isEqualTo("None");
|
||||||
assertThat(attributes.get("status")).isEqualTo(999);
|
assertThat(attributes.get("status")).isEqualTo(999);
|
||||||
}
|
}
|
||||||
|
@ -77,48 +78,74 @@ 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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
||||||
assertThat(modelAndView).isNull();
|
assertThat(modelAndView).isNull();
|
||||||
assertThat(attributes.get("exception")).isNull();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void servletError() {
|
void servletErrorWithDetail() {
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
||||||
assertThat(attributes.get("exception")).isNull();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void servletMessage() {
|
void servletErrorWithoutDetail() {
|
||||||
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
|
this.request.setAttribute("javax.servlet.error.exception", ex);
|
||||||
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false);
|
||||||
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
|
||||||
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
|
assertThat(attributes.get("message").toString()).contains("An error occurred");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void servletMessageWithDetail() {
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
assertThat(attributes.get("exception")).isNull();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void nullMessage() {
|
void servletMessageWithoutDetail() {
|
||||||
|
this.request.setAttribute("javax.servlet.error.message", "Test");
|
||||||
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false);
|
||||||
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
|
assertThat(attributes.get("message")).asString().contains("An error occurred");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
assertThat(attributes.get("exception")).isNull();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nullExceptionMessageAndServletMessage() {
|
||||||
|
this.request.setAttribute("javax.servlet.error.exception", new RuntimeException());
|
||||||
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
|
assertThat(attributes.get("message")).isEqualTo("No message available");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unwrapServletException() {
|
void unwrapServletException() {
|
||||||
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(wrapped);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(wrapped);
|
||||||
assertThat(attributes.get("exception")).isNull();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
assertThat(attributes.get("message")).isEqualTo("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,65 +153,81 @@ 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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(error);
|
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(error);
|
||||||
assertThat(attributes.get("exception")).isNull();
|
assertThat(attributes.containsKey("exception")).isFalse();
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test error");
|
assertThat(attributes.get("message")).isEqualTo("Test error");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void extractBindingResultErrors() {
|
void withBindingErrors() {
|
||||||
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);
|
testBindingResult(bindingResult, ex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void extractMethodArgumentNotValidExceptionBindingResultErrors() {
|
void withoutBindingErrors() {
|
||||||
|
BindingResult bindingResult = new MapBindingResult(Collections.singletonMap("a", "b"), "objectName");
|
||||||
|
bindingResult.addError(new ObjectError("c", "d"));
|
||||||
|
Exception ex = new BindException(bindingResult);
|
||||||
|
testBindingResult(bindingResult, ex, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withMethodArgumentNotValidExceptionBindingErrors() {
|
||||||
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(null, bindingResult);
|
||||||
testBindingResult(bindingResult, ex);
|
testBindingResult(bindingResult, ex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testBindingResult(BindingResult bindingResult, Exception ex) {
|
private void testBindingResult(BindingResult bindingResult, Exception ex, boolean includeDetails) {
|
||||||
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, false,
|
||||||
assertThat(attributes.get("message")).isEqualTo("Validation failed for object='objectName'. Error count: 1");
|
includeDetails);
|
||||||
|
if (includeDetails) {
|
||||||
|
assertThat(attributes.get("message"))
|
||||||
|
.isEqualTo("Validation failed for object='objectName'. Error count: 1");
|
||||||
assertThat(attributes.get("errors")).isEqualTo(bindingResult.getAllErrors());
|
assertThat(attributes.get("errors")).isEqualTo(bindingResult.getAllErrors());
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
assertThat(attributes.get("message")).isEqualTo("Validation failed");
|
||||||
|
assertThat(attributes.containsKey("errors")).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void withExceptionAttribute() {
|
void withExceptionAttribute() {
|
||||||
DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes(true);
|
DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes(true);
|
||||||
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);
|
Map<String, Object> attributes = errorAttributes.getErrorAttributes(this.webRequest, false, true);
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void trace() {
|
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, true, false);
|
||||||
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void noTrace() {
|
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false);
|
||||||
assertThat(attributes.get("trace")).isNull();
|
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);
|
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(this.webRequest, false, false);
|
||||||
assertThat(attributes.get("path")).isEqualTo("path");
|
assertThat(attributes.get("path")).isEqualTo("path");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -34,8 +34,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
*
|
*
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = { "server.error.include-details=always" })
|
||||||
class SampleActuatorCustomSecurityApplicationTests extends AbstractSampleActuatorCustomSecurityTests {
|
class SampleActuatorCustomSecurityApplicationTests extends AbstractSampleActuatorCustomSecurityTests {
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "server.error.include-details=always" })
|
||||||
class SampleActuatorUiApplicationTests {
|
class SampleActuatorUiApplicationTests {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
Loading…
Reference in New Issue