Commit 3f88906b authored by Stephane Nicoll's avatar Stephane Nicoll

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
parent fdd501c9
/*
* 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,24 +79,36 @@ 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());
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();
}
if (includeStackTrace) {
addStackTrace(errorAttributes, error);
return HttpStatus.INTERNAL_SERVER_ERROR;
}
private String determineMessage(Throwable error) {
if (error instanceof WebExchangeBindException) {
return error.getMessage();
}
addErrorMessage(errorAttributes, error);
if (error instanceof ResponseStatusException) {
HttpStatus errorStatus = ((ResponseStatusException) error).getStatus();
errorAttributes.put("status", errorStatus.value());
errorAttributes.put("error", errorStatus.getReasonPhrase());
return ((ResponseStatusException) error).getReason();
}
else {
errorAttributes.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
errorAttributes.put("error",
HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
return error.getMessage();
}
private Throwable determineException(Throwable error) {
if (error instanceof ResponseStatusException) {
return error.getCause() != null ? error.getCause() : error;
}
return errorAttributes;
return error;
}
private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
......@@ -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) {
......
/*
* 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");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment