23

How can I customize the response status code and the data in the response body if an exception occurs in a Spring Boot web application?

I have created a web app that throws a custom exception if something unexpected occurs due to some bad internal state. Consequently, the response body of the request that triggered the error looks something like:

HTTP/1.1 500 Internal Server Error
{
    "timestamp": 1412685688268,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "com.example.CustomException",
    "message": null,
    "path": "/example"
}

Now, I would like to change the status code and set the fields in the response body. One solution that crossed my mind was something like:

@ControllerAdvice
class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    ErrorMessage handleBadCredentials(CustomException e) {
        return new ErrorMessage("Bad things happened");
    }
}

@XmlRootElement
public class ErrorMessage(
    private String error;

    public ErrorMessage() {
    }

    public ErrorMessage(String error) {
        this.error = error;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }
)

However, that created (as suspected) a completely different response:

HTTP/1.1 400 Bad Request
{
    "error": "Bad things happened"
}
3
  • @zeroflagL Preferably, I would like to customize the response generated by Spring Boot (if possible). Implementing a complete custom solution (like the one provided in the question) works, but is less reusable between different projects. Commented Oct 7, 2014 at 13:44
  • 2
    It's completely up to you if the custom solution is reusable or not. FWIW: The resposne body is assembled by DefaultErrorAttributes#getErrorAttributes. You could inject that class into your CustomResponseEntityExceptionHandler. Commented Oct 7, 2014 at 14:05
  • @zeroflagL I failed to get your suggestion to work (scroll down in the issue I filed). However, I did get help in finding a solution, see my answer below (or read my blog post). Commented Oct 19, 2014 at 10:15

2 Answers 2

35

As @zeroflagL mentioned, Spring Boot fabricates the "standard" error response body in org.springframework.boot.autoconfigure.web.DefaultErrorAttributes. Similar to your needs, I wanted to leverage all of that, but simply augment one more "type" field that was provided by some of my exceptions.

I did that by implementing a Component that sub-classed DefaultErrorAttributes. Spring Boot automatically picked it up and used mine instead of the default.

@Component
public class ExtendedErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, 
                                                  boolean includeStackTrace) {
        final Map<String, Object> errorAttributes = 
            super.getErrorAttributes(requestAttributes, 
                                     includeStackTrace);

        final Throwable error = super.getError(requestAttributes);
        if (error instanceof TypeProvider) {
            final TypeProvider typeProvider = (TypeProvider) error;
            errorAttributes.put("type", typeProvider.getTypeIdentifier());
        }

        return errorAttributes;
    }
}

With that, I get an augmented JSON response body, such as

{
  "timestamp": 1488058582764,
  "status": 429,
  "error": "Too Many Requests",
  "exception": "com.example.ExternalRateLimitException",
  "message": "DAILY_LIMIT: too many requests",
  "path": "/api/lookup",
  "type": "DAILY_LIMIT"
}
Sign up to request clarification or add additional context in comments.

3 Comments

Great answer! Can also easily be modified to remove the 'exception' attribute from the error response in production deployments, if you aren't interested in leaking those kind of details.
As of now (and probably since Spring Boot 2) you need to extend org.springframework.boot.web.servlet.error.DefaultErrorAttributes which has a org.springframework.web.context.request.WebRequest in the parameters instead of RequestAttributes.
Also, since spring boot 2 getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) is deprecated. use getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions errorAttributeOptions)
13

The http response status code can be changed by using the HttpServletResponse.sendError(int) method, e.g.

@ExceptionHandler
void handleIllegalArgumentException(IllegalArgumentException e, HttpServletResponse response) throws IOException {
    response.sendError(HttpStatus.BAD_REQUEST.value());
}

Alternatively, you can declare the exception type in the @ExceptionHandler annotation if you have two or more exceptions to generate the same response status:

@ExceptionHandler({IllegalArgumentException.class, NullPointerException.class})
void handleBadRequests(HttpServletResponse response) throws IOException {
    response.sendError(HttpStatus.BAD_REQUEST.value());
}

More information can be found in my blog post.

1 Comment

It is worth noting that this sets only the response status code; the rest of the response message stays the same. While is is not quite what the asker was asking, it is exactly what I needed for my own use case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.