Fix handling of ResponseStatusException
This commit updates DefaultErrorAttributes to handle ResponseStatusException explicitly. This exception is used in a WebFlux application to signal that the processing of the query has failed with an HTTP status code and a reason phrase. The latter is now properly mapped to the `message` attribute of the response body. Closes gh-11614
This commit is contained in:
parent
fdd501c943
commit
3f88906b97
|
@ -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");
|
* 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.
|
||||||
|
@ -25,6 +25,7 @@ import java.util.Map;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.ObjectError;
|
import org.springframework.validation.ObjectError;
|
||||||
|
import org.springframework.web.bind.support.WebExchangeBindException;
|
||||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
@ -44,6 +45,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @see ErrorAttributes
|
* @see ErrorAttributes
|
||||||
*/
|
*/
|
||||||
|
@ -77,26 +79,38 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
||||||
errorAttributes.put("timestamp", new Date());
|
errorAttributes.put("timestamp", new Date());
|
||||||
errorAttributes.put("path", request.path());
|
errorAttributes.put("path", request.path());
|
||||||
Throwable error = getError(request);
|
Throwable error = getError(request);
|
||||||
if (this.includeException) {
|
HttpStatus errorStatus = determineHttpStatus(error);
|
||||||
errorAttributes.put("exception", error.getClass().getName());
|
errorAttributes.put("status", errorStatus.value());
|
||||||
}
|
errorAttributes.put("error", errorStatus.getReasonPhrase());
|
||||||
if (includeStackTrace) {
|
errorAttributes.put("message", determineMessage(error));
|
||||||
addStackTrace(errorAttributes, error);
|
handleException(errorAttributes, determineException(error), includeStackTrace);
|
||||||
}
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
return errorAttributes;
|
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<String, Object> errorAttributes, Throwable error) {
|
private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
|
||||||
StringWriter stackTrace = new StringWriter();
|
StringWriter stackTrace = new StringWriter();
|
||||||
error.printStackTrace(new PrintWriter(stackTrace));
|
error.printStackTrace(new PrintWriter(stackTrace));
|
||||||
|
@ -104,8 +118,14 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
||||||
errorAttributes.put("trace", stackTrace.toString());
|
errorAttributes.put("trace", stackTrace.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
|
private void handleException(Map<String, Object> errorAttributes,
|
||||||
errorAttributes.put("message", error.getMessage());
|
Throwable error, boolean includeStackTrace) {
|
||||||
|
if (this.includeException) {
|
||||||
|
errorAttributes.put("exception", error.getClass().getName());
|
||||||
|
}
|
||||||
|
if (includeStackTrace) {
|
||||||
|
addStackTrace(errorAttributes, error);
|
||||||
|
}
|
||||||
if (error instanceof BindingResult) {
|
if (error instanceof BindingResult) {
|
||||||
BindingResult result = (BindingResult) error;
|
BindingResult result = (BindingResult) error;
|
||||||
if (result.getErrorCount() > 0) {
|
if (result.getErrorCount() > 0) {
|
||||||
|
|
|
@ -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");
|
* 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,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
* Tests for {@link DefaultErrorAttributes}.
|
* Tests for {@link DefaultErrorAttributes}.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
public class DefaultErrorAttributesTests {
|
public class DefaultErrorAttributesTests {
|
||||||
|
|
||||||
|
@ -125,6 +126,39 @@ public class DefaultErrorAttributesTests {
|
||||||
assertThat(attributes.get("message")).isEqualTo("Test");
|
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<String, Object> 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<String, Object> 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
|
@Test
|
||||||
public void notIncludeTrace() {
|
public void notIncludeTrace() {
|
||||||
RuntimeException ex = new RuntimeException("Test");
|
RuntimeException ex = new RuntimeException("Test");
|
||||||
|
|
Loading…
Reference in New Issue