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");
|
||||
* 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;
|
|||
* </ul>
|
||||
*
|
||||
* @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<String, Object> 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<String, Object> errorAttributes, Throwable error) {
|
||||
errorAttributes.put("message", error.getMessage());
|
||||
private void handleException(Map<String, Object> 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) {
|
||||
|
|
|
@ -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<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
|
||||
public void notIncludeTrace() {
|
||||
RuntimeException ex = new RuntimeException("Test");
|
||||
|
|
Loading…
Reference in New Issue