diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributes.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributes.java index b4852a78d60..176e351e975 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributes.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import java.util.Map; import org.springframework.http.HttpStatus; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; +import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; @@ -44,6 +45,7 @@ import org.springframework.web.server.ServerWebExchange; * * * @author Brian Clozel + * @author Stephane Nicoll * @since 2.0.0 * @see ErrorAttributes */ @@ -77,26 +79,38 @@ public class DefaultErrorAttributes implements ErrorAttributes { errorAttributes.put("timestamp", new Date()); errorAttributes.put("path", request.path()); Throwable error = getError(request); - if (this.includeException) { - errorAttributes.put("exception", error.getClass().getName()); - } - if (includeStackTrace) { - addStackTrace(errorAttributes, error); - } - addErrorMessage(errorAttributes, error); - if (error instanceof ResponseStatusException) { - HttpStatus errorStatus = ((ResponseStatusException) error).getStatus(); - errorAttributes.put("status", errorStatus.value()); - errorAttributes.put("error", errorStatus.getReasonPhrase()); - } - else { - errorAttributes.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value()); - errorAttributes.put("error", - HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()); - } + HttpStatus errorStatus = determineHttpStatus(error); + errorAttributes.put("status", errorStatus.value()); + errorAttributes.put("error", errorStatus.getReasonPhrase()); + errorAttributes.put("message", determineMessage(error)); + handleException(errorAttributes, determineException(error), includeStackTrace); return errorAttributes; } + private HttpStatus determineHttpStatus(Throwable error) { + if (error instanceof ResponseStatusException) { + return ((ResponseStatusException) error).getStatus(); + } + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + private String determineMessage(Throwable error) { + if (error instanceof WebExchangeBindException) { + return error.getMessage(); + } + if (error instanceof ResponseStatusException) { + return ((ResponseStatusException) error).getReason(); + } + return error.getMessage(); + } + + private Throwable determineException(Throwable error) { + if (error instanceof ResponseStatusException) { + return error.getCause() != null ? error.getCause() : error; + } + return error; + } + private void addStackTrace(Map errorAttributes, Throwable error) { StringWriter stackTrace = new StringWriter(); error.printStackTrace(new PrintWriter(stackTrace)); @@ -104,8 +118,14 @@ public class DefaultErrorAttributes implements ErrorAttributes { errorAttributes.put("trace", stackTrace.toString()); } - private void addErrorMessage(Map errorAttributes, Throwable error) { - errorAttributes.put("message", error.getMessage()); + private void handleException(Map errorAttributes, + Throwable error, boolean includeStackTrace) { + if (this.includeException) { + errorAttributes.put("exception", error.getClass().getName()); + } + if (includeStackTrace) { + addStackTrace(errorAttributes, error); + } if (error instanceof BindingResult) { BindingResult result = (BindingResult) error; if (result.getErrorCount() > 0) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributesTests.java index 2f37dcf922a..34d75b039bb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorAttributesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link DefaultErrorAttributes}. * * @author Brian Clozel + * @author Stephane Nicoll */ public class DefaultErrorAttributesTests { @@ -125,6 +126,39 @@ public class DefaultErrorAttributesTests { assertThat(attributes.get("message")).isEqualTo("Test"); } + @Test + public void processResponseStatusException() { + RuntimeException nested = new RuntimeException("Test"); + ResponseStatusException error = new ResponseStatusException( + HttpStatus.BAD_REQUEST, "invalid request", nested); + this.errorAttributes = new DefaultErrorAttributes(true); + MockServerHttpRequest request = MockServerHttpRequest.get("/test").build(); + ServerRequest serverRequest = buildServerRequest(request, error); + Map attributes = this.errorAttributes + .getErrorAttributes(serverRequest, false); + assertThat(attributes.get("status")).isEqualTo(400); + assertThat(attributes.get("message")).isEqualTo("invalid request"); + assertThat(attributes.get("exception")) + .isEqualTo(RuntimeException.class.getName()); + assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error); + } + + @Test + public void processResponseStatusExceptionWithNoNestedCause() { + ResponseStatusException error = new ResponseStatusException( + HttpStatus.NOT_ACCEPTABLE, "could not process request"); + this.errorAttributes = new DefaultErrorAttributes(true); + MockServerHttpRequest request = MockServerHttpRequest.get("/test").build(); + ServerRequest serverRequest = buildServerRequest(request, error); + Map attributes = this.errorAttributes + .getErrorAttributes(serverRequest, false); + assertThat(attributes.get("status")).isEqualTo(406); + assertThat(attributes.get("message")).isEqualTo("could not process request"); + assertThat(attributes.get("exception")) + .isEqualTo(ResponseStatusException.class.getName()); + assertThat(this.errorAttributes.getError(serverRequest)).isSameAs(error); + } + @Test public void notIncludeTrace() { RuntimeException ex = new RuntimeException("Test");