Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot customize http error response?

Tags:

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"
}
like image 858
matsev Avatar asked Oct 07 '14 13:10

matsev


People also ask

How do I return a custom error message in Web API spring boot?

The most basic way of returning an error message from a REST API is to use the @ResponseStatus annotation. We can add the error message in the annotation's reason field. Although we can only return a generic error message, we can specify exception-specific error messages.

How do I create a custom exception in spring boot?

Define a class that extends the RuntimeException class. You can define the @ExceptionHandler method to handle the exceptions as shown. This method should be used for writing the Controller Advice class file. Now, use the code given below to throw the exception from the API.

How do I change the response status in spring boot?

If we want to specify the response status of a controller method, we need to annotate that method with the @ResponseStatus annotation. It has two interchangeable arguments for the desired response type: code and value. Note, that when we set reason, Spring calls HttpServletResponse. sendError().


2 Answers

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"
}
like image 170
itzg Avatar answered Oct 10 '22 18:10

itzg


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.

like image 29
matsev Avatar answered Oct 10 '22 16:10

matsev