Spring Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
REST Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

This tutorial will illustrate how to implement Exception Handling with Spring for a REST API. We'll also get a bit of historical overview and see which new options the different versions introduced.

Before Spring 3.2, the two main approaches to handling exceptions in a Spring MVC application were HandlerExceptionResolver or the @ExceptionHandler annotation. Both have some clear downsides.

Since 3.2, we've had the @ControllerAdvice annotation to address the limitations of the previous two solutions and to promote a unified exception handling throughout a whole application.

Now Spring 5 introduces the ResponseStatusException class — a fast way for basic error handling in our REST APIs.

All of these do have one thing in common: They deal with the separation of concerns very well. The app can throw exceptions normally to indicate a failure of some kind, which will then be handled separately.

Finally, we'll see what Spring Boot brings to the table and how we can configure it to suit our needs.

Further reading:

Custom Error Message Handling for REST API

Implement a Global Exception Handler for a REST API with Spring.

Guide to Spring Data REST Validators

Quick and practical guide to Spring Data REST Validators

Spring MVC Custom Validation

Learn how to build a custom validation annotation and use it in Spring MVC.

2. Solution 1: the Controller-Level @ExceptionHandler

The first solution works at the @Controller level. We will define a method to handle exceptions and annotate that with @ExceptionHandler:

public class FooController{
    
    //...
    @ExceptionHandler({ CustomException1.class, CustomException2.class })
    public void handleException() {
        //
    }
}

This approach has a major drawback: The @ExceptionHandler annotated method is only active for that particular Controller, not globally for the entire application. Of course, adding this to every controller makes it not well suited for a general exception handling mechanism.

We can work around this limitation by having all Controllers extend a Base Controller class.

However, this solution can be a problem for applications where, for whatever reason, that isn't possible. For example, the Controllers may already extend from another base class, which may be in another jar or not directly modifiable, or may themselves not be directly modifiable.

Next, we'll look at another way to solve the exception handling problem — one that is global and doesn't include any changes to existing artifacts such as Controllers.

3. Solution 2: the HandlerExceptionResolver

The second solution is to define an HandlerExceptionResolver. This will resolve any exception thrown by the application. It will also allow us to implement a uniform exception handling mechanism in our REST API.

Before going for a custom resolver, let's go over the existing implementations.

3.1. ExceptionHandlerExceptionResolver

This resolver was introduced in Spring 3.1 and is enabled by default in the DispatcherServlet. This is actually the core component of how the @ExceptionHandler mechanism presented earlier works.

3.2. DefaultHandlerExceptionResolver

This resolver was introduced in Spring 3.0, and it's enabled by default in the DispatcherServlet.

It's used to resolve standard Spring exceptions to their corresponding HTTP Status Codes, namely Client error 4xx and Server error 5xx status codes. Here's the full list of the Spring Exceptions it handles and how they map to status codes.

While it does set the Status Code of the Response properly, one limitation is that it doesn't set anything to the body of the Response. And for a REST API — the Status Code is really not enough information to present to the Client — the response has to have a body as well, to allow the application to give additional information about the failure.

This can be solved by configuring view resolution and rendering error content through ModelAndView, but the solution is clearly not optimal. That's why Spring 3.2 introduced a better option that we'll discuss in a later section.

3.3. ResponseStatusExceptionResolver

This resolver was also introduced in Spring 3.0 and is enabled by default in the DispatcherServlet.

Its main responsibility is to use the @ResponseStatus annotation available on custom exceptions and to map these exceptions to HTTP status codes.

Such a custom exception may look like:

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class MyResourceNotFoundException extends RuntimeException {
    public MyResourceNotFoundException() {
        super();
    }
    public MyResourceNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
    public MyResourceNotFoundException(String message) {
        super(message);
    }
    public MyResourceNotFoundException(Throwable cause) {
        super(cause);
    }
}

The same as the DefaultHandlerExceptionResolver, this resolver is limited in the way it deals with the body of the response — it does map the Status Code on the response, but the body is still null.

3.4. SimpleMappingExceptionResolver and AnnotationMethodHandlerExceptionResolver

The SimpleMappingExceptionResolver has been around for quite some time. It comes out of the older Spring MVC model and is not very relevant for a REST Service. We basically use it to map exception class names to view names.

The AnnotationMethodHandlerExceptionResolver was introduced in Spring 3.0 to handle exceptions through the @ExceptionHandler annotation but has been deprecated by ExceptionHandlerExceptionResolver as of Spring 3.2.

3.5. Custom HandlerExceptionResolver

The combination of DefaultHandlerExceptionResolver and ResponseStatusExceptionResolver goes a long way toward providing a good error handling mechanism for a Spring RESTful Service. The downside is, as mentioned before, no control over the body of the response.

Ideally, we'd like to be able to output either JSON or XML, depending on what format the client has asked for (via the Accept header).

This alone justifies creating a new, custom exception resolver:

@Component
public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {

    @Override
    protected ModelAndView doResolveException(
      HttpServletRequest request, 
      HttpServletResponse response, 
      Object handler, 
      Exception ex) {
        try {
            if (ex instanceof IllegalArgumentException) {
                return handleIllegalArgument(
                  (IllegalArgumentException) ex, response, handler);
            }
            ...
        } catch (Exception handlerException) {
            logger.warn("Handling of [" + ex.getClass().getName() + "] 
              resulted in Exception", handlerException);
        }
        return null;
    }

    private ModelAndView 
      handleIllegalArgument(IllegalArgumentException ex, HttpServletResponse response) 
      throws IOException {
        response.sendError(HttpServletResponse.SC_CONFLICT);
        String accept = request.getHeader(HttpHeaders.ACCEPT);
        ...
        return new ModelAndView();
    }
}

One detail to notice here is that we have access to the request itself, so we can consider the value of the Accept header sent by the client.

For example, if the client asks for application/json, then, in the case of an error condition, we'd want to make sure we return a response body encoded with application/json.

The other important implementation detail is that we return a ModelAndView — this is the body of the response, and it will allow us to set whatever is necessary on it.

This approach is a consistent and easily configurable mechanism for the error handling of a Spring REST Service.

It does, however, have limitations: It's interacting with the low-level HtttpServletResponse and fits into the old MVC model that uses ModelAndView, so there's still room for improvement.

4. Solution 3: @ControllerAdvice

Spring 3.2 brings support for a global @ExceptionHandler with the @ControllerAdvice annotation.

This enables a mechanism that breaks away from the older MVC model and makes use of ResponseEntity along with the type safety and flexibility of @ExceptionHandler:

@ControllerAdvice
public class RestResponseEntityExceptionHandler 
  extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value 
      = { IllegalArgumentException.class, IllegalStateException.class })
    protected ResponseEntity<Object> handleConflict(
      RuntimeException ex, WebRequest request) {
        String bodyOfResponse = "This should be application specific";
        return handleExceptionInternal(ex, bodyOfResponse, 
          new HttpHeaders(), HttpStatus.CONFLICT, request);
    }
}

The@ControllerAdvice annotation allows us to consolidate our multiple, scattered @ExceptionHandlers from before into a single, global error handling component.

The actual mechanism is extremely simple but also very flexible:

  • It gives us full control over the body of the response as well as the status code.
  • It provides mapping of several exceptions to the same method, to be handled together.
  • It makes good use of the newer RESTful ResposeEntity response.

One thing to keep in mind here is to match the exceptions declared with @ExceptionHandler to the exception used as the argument of the method.

If these don't match, the compiler will not complain — no reason it should — and Spring will not complain either.

However, when the exception is actually thrown at runtime, the exception resolving mechanism will fail with:

java.lang.IllegalStateException: No suitable resolver for argument [0] [type=...]
HandlerMethod details: ...

5. Solution 4: ResponseStatusException (Spring 5 and Above)

Spring 5 introduced the ResponseStatusException class.

We can create an instance of it providing an HttpStatus and optionally a reason and a cause:

@GetMapping(value = "/{id}")
public Foo findById(@PathVariable("id") Long id, HttpServletResponse response) {
    try {
        Foo resourceById = RestPreconditions.checkFound(service.findOne(id));

        eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
        return resourceById;
     }
    catch (MyResourceNotFoundException exc) {
         throw new ResponseStatusException(
           HttpStatus.NOT_FOUND, "Foo Not Found", exc);
    }
}

What are the benefits of using ResponseStatusException?

  • Excellent for prototyping: We can implement a basic solution quite fast.
  • One type, multiple status codes: One exception type can lead to multiple different responses. This reduces tight coupling compared to the @ExceptionHandler.
  • We won't have to create as many custom exception classes.
  • We have more control over exception handling since the exceptions can be created programmatically.

And what about the tradeoffs?

  • There's no unified way of exception handling: It's more difficult to enforce some application-wide conventions as opposed to @ControllerAdvice, which provides a global approach.
  • Code duplication: We may find ourselves replicating code in multiple controllers.

We should also note that it's possible to combine different approaches within one application.

For example, we can implement a @ControllerAdvice globally but also ResponseStatusExceptions locally.

However, we need to be careful: If the same exception can be handled in multiple ways, we may notice some surprising behavior. A possible convention is to handle one specific kind of exception always in one way.

For more details and further examples, see our tutorial on ResponseStatusException.

6. Handle the Access Denied in Spring Security

The Access Denied occurs when an authenticated user tries to access resources that he doesn't have enough authorities to access.

6.1. MVC — Custom Error Page

First, let's look at the MVC style of the solution and see how to customize an error page for Access Denied.

The XML configuration:

<http>
    <intercept-url pattern="/admin/*" access="hasAnyRole('ROLE_ADMIN')"/>   
    ... 
    <access-denied-handler error-page="/my-error-page" />
</http>

And the Java configuration:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/admin/*").hasAnyRole("ROLE_ADMIN")
        ...
        .and()
        .exceptionHandling().accessDeniedPage("/my-error-page");
}

When users try to access a resource without having enough authorities, they will be redirected to “/my-error-page”.

6.2. Custom AccessDeniedHandler

Next, let's see how to write our custom AccessDeniedHandler:

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle
      (HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex) 
      throws IOException, ServletException {
        response.sendRedirect("/my-error-page");
    }
}

And now let's configure it using XML configuration:

<http>
    <intercept-url pattern="/admin/*" access="hasAnyRole('ROLE_ADMIN')"/> 
    ...
    <access-denied-handler ref="customAccessDeniedHandler" />
</http>

0r using Java configuration:

@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/admin/*").hasAnyRole("ROLE_ADMIN")
        ...
        .and()
        .exceptionHandling().accessDeniedHandler(accessDeniedHandler)
}

Note how in our CustomAccessDeniedHandler, we can customize the response as we wish by redirecting or displaying a custom error message.

6.3. REST and Method-Level Security

Finally, let's see how to handle method-level security @PreAuthorize, @PostAuthorize, and @Secure Access Denied.

Of course, we'll use the global exception handling mechanism that we discussed earlier to handle the AccessDeniedException as well:

@ControllerAdvice
public class RestResponseEntityExceptionHandler 
  extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ AccessDeniedException.class })
    public ResponseEntity<Object> handleAccessDeniedException(
      Exception ex, WebRequest request) {
        return new ResponseEntity<Object>(
          "Access denied message here", new HttpHeaders(), HttpStatus.FORBIDDEN);
    }
    
    ...
}

7. Spring Boot Support

Spring Boot provides an ErrorController implementation to handle errors in a sensible way.

In a nutshell, it serves a fallback error page for browsers (a.k.a. the Whitelabel Error Page) and a JSON response for RESTful, non-HTML requests:

{
    "timestamp": "2019-01-17T16:12:45.977+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Error processing the request!",
    "path": "/my-endpoint-with-exceptions"
}

As usual, Spring Boot allows configuring these features with properties:

  • server.error.whitelabel.enabled: can be used to disable the Whitelabel Error Page and rely on the servlet container to provide an HTML error message
  • server.error.include-stacktrace: with an always value; includes the stacktrace in both the HTML and the JSON default response

Apart from these properties, we can provide our own view-resolver mapping for /error, overriding the Whitelabel Page.

We can also customize the attributes that we want to show in the response by including an ErrorAttributes bean in the context. We can extend the DefaultErrorAttributes class provided by Spring Boot to make things easier:

@Component
public class MyCustomErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(
      WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> errorAttributes = 
          super.getErrorAttributes(webRequest, includeStackTrace);
        errorAttributes.put("locale", webRequest.getLocale()
            .toString());
        errorAttributes.remove("error");

        //...

        return errorAttributes;
    }
}

If we want to go further and define (or override) how the application will handle errors for a particular content type, we can register an ErrorController bean.

Again, we can make use of the default BasicErrorController provided by Spring Boot to help us out.

For example, imagine we want to customize how our application handles errors triggered in XML endpoints. All we have to do is define a public method using the @RequestMapping, and stating it produces application/xml media type:

@Component
public class MyErrorController extends BasicErrorController {

    public MyErrorController(ErrorAttributes errorAttributes) {
        super(errorAttributes, new ErrorProperties());
    }

    @RequestMapping(produces = MediaType.APPLICATION_XML_VALUE)
    public ResponseEntity<Map<String, Object>> xmlError(HttpServletRequest request) {
        
    // ...

    }
}

8. Conclusion

This article discussed several ways to implement an exception handling mechanism for a REST API in Spring, starting with the older mechanism and continuing with the Spring 3.2 support and into 4.x and 5.x.

As always, the code presented in this article is available over on GitHub.

For the Spring Security-related code, you can check the spring-security-rest module.

Spring bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
REST bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
21 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Karlis Zigurs
Karlis Zigurs
4 years ago

All of these are good for handling specific exceptions, but don’t really work if you want to implement a uniform way for the controller to handle both the response body and returned error code in a uniform way (uniform format of responses is something that would be expected from any API). To achieve that, take a look at: https://github.com/karliszigurs/QuickSearchServer/blob/master/src/main/java/com/zigurs/karlis/utils/search/server/controller/JsonApiController.java Annotate your business logic exceptions with ResponseType (forbidden, not found, missing, etc…) and you can pick it up by resolving annotation in the exception handler easily (whether in controller or controllerAdvice). The only edge case that remains in this case is… Read more »

Eugen Paraschiv
4 years ago
Reply to  Karlis Zigurs

Hey Karlis – using custom exceptions and annotating these is one to go. I personally prefer not to do that, and actually see this solution (keeping all of this logic in one place) as the more uniform way of doing things.
Of course this solution works perfectly fine with custom business logic exception – and good exception does benefit a lot from semantically rich exceptions. These go into the one, main class as well.
Thanks for the feedback though – definitely helpful.
Cheers,
Eugen.

Carlos Zela Bueno
Carlos Zela Bueno
4 years ago

Hi $Eugen I’m try to follow your guideline but I have a error trying to override a spring exception with a responseBody customized. Mi class look as: @EnableWebMvc @ControllerAdvice class ResponseEntityBusinessExceptionHandler extends ResponseEntityExceptionHandler ….. @Exception(MyException.class) public ResponseEntity handleMyException (Throwable ex, HttpServletRequest request){ return new ResponseEntity( “Any message for my exception”, new HttpHeaders(), HttpStatus.BAD_REQUEST); // Here all is ok. Below is the error …. @Override protected ResponseEntity handleHttpMediaTypeNotAcceptable( HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { return handleExceptionInternal(ex, “My Body for Not Acceptable, headers, status,request) ; } === I dont know why when override or handle a spring exception, the… Read more »

Grzegorz Piwowarek
Grzegorz Piwowarek
4 years ago

Carlos, I am not sure if I got the problem correctly. handleMyException() works correctly and below methods don’t?

Carlos Zela Bueno
Carlos Zela Bueno
4 years ago

Grzegorz, correct..When use @ExceptionHandler for my exception classes, it’s working fine and return my response Entity. However, when I override any spring handle exception such as handleMediaTypeNotAcceptable or handleMissingPathParameter apparently I received spring response entity.
In abstract..I take handle exception by default of spring but I want a customized response, change response body to handle Exception of spring and add a body to handle that by default doesn’t have a body.
I hope I have been clearer now.

Thanks

Grzegorz Piwowarek
Grzegorz Piwowarek
4 years ago

Ok, with such a small snippet it’s really hard to tell anything. Could you prepare a repo/PR on GitHub with the whole code to investigate?

Carlos Zela Bueno
Carlos Zela Bueno
4 years ago

Sure!
https://github.com/czelabueno/
I’ve written more details in README.md

Put focus in RestAPIControllerAdvice.handleHttpMediaTypeNotAcceptable method.
I’ve noted that when I sent a String body it works fine. But when I use my ResponseError bean spring send the exception. My override haven’t effect when use responseError bean, only to String message.

Thank you in advance!

Grzegorz Piwowarek
Grzegorz Piwowarek
4 years ago

Carlos,
I can’t seem to reproduce the problem. Whenever I am sending the mentioned header, I simply get an empty response request does not get matched.

Grzegorz Piwowarek
Grzegorz Piwowarek
4 years ago

Carlos,
The problem is that you are overriding the method that is supposed to handleT

jd
jd
4 years ago

Good post. Thanks.

How can you catch the exception generated during the JSON parsing in POST operation for instance? I’ve trying to generate my own response, unsuccessfully. Somehow that exception is caught by someone else before it gets to the generic @ControllerAdvice controller.

Eugen Paraschiv
4 years ago
Reply to  jd

Hey JD – that’s a good question, but one I’ll only be able to answer at a high level without more details.
So – if your exception is caught by some other piece of code, you’ll have to carefully debug and see who’s catching it. The @ControllerAdvice annotated global handler will only catch exceptions that are actually thrown by the underlying code. If an exception is already handled, naturally – it won’t be caught by the global handler.
Hope that helps point you in the right direction.
Cheers,
Eugen.

jd
jd
4 years ago

Found it! You just need to do:

@Override
protected ResponseEntity handleHttpMessageNotReadable(final HttpMessageNotReadableException exception,
final HttpHeaders headers, final HttpStatus status, final WebRequest request) {

ApiError apiError = new ApiError( … your constructor logic …. );

return new ResponseEntity(apiError, apiError.getStatus());
}

In your @ControllerAdvice controller.

Thanks a lot.

Eugen Paraschiv
4 years ago
Reply to  jd

It looks like the actual exception that was being thrown was the HttpMessageNotReadableException. Cool, I’m glad you were able to sort it out.

Illia Trach
Illia Trach
3 years ago

Is it OK that all exception trace is being logged and printed in to console?
I have implemented @ControllerAdvice.

Eugen Paraschiv
3 years ago
Reply to  Illia Trach

Hey Illia,
To make sure I’m understanding you correctly – you’re asking if it’s OK to handle these exceptions by printing them to the console? Or are you also handling them separately, but also logging them?

Generally speaking it’s OK to log them, sure (although a TRACE level is not the way to go); but logging the exception is usually not enough – you most likely also need to return something back to the client to make them aware of the error.

Hope that helps. Cheers,
Eugen.

Illia Trach
Illia Trach
3 years ago

In standard scenario when exception occurs and we are not expecting it all info is being printed into console. But in this case we are expecting for it and handling it. I don’t wont exception trace in my console.

Eugen Paraschiv
3 years ago
Reply to  Illia Trach

Well Illia, if the exception is entirely unexpected, and you really don’t have any way to handle it – yes, you at least need to log in (not just output it to the console, log it).

That being said, even in that case you need to make sure the system fails in a way that’s a decent experience for the user. For example, filling the browser window with a large stacktrace is not good enough.

Hope that helps. Cheers,
Eugen.

Illia Trach
Illia Trach
3 years ago

Solved! It came to me while i was writing this latter 🙂 Forgive me, Eugen, if my explanation is poor. I really appreciate your tutorials. Here is console log of handled custom exception from this tutorial http://www.baeldung.com/spring-security-registration web – 2017-01-10 20:38:01,325 [http-nio-8081-exec-2] ERROR o.b.w.e.RestResponseEntityExceptionHandler – 409 Status Code o.b.w.e.UserAlreadyExistException: There is an account with that email adress: [email protected] at o.b.s.UserService.registerNewUserAccount(UserService.java:64) at s.r.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java) at s.r.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at s.r.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at j.l.r.Method.invoke(Method.java:497) … 86 frames truncated So, I didn’t wont this type of error. I wanted something like this: 2017-01-10 20:38:01,325 Client Error – 409 Status Code. Sending response: “There is an account with… Read more »

Igor Dmitriev
Igor Dmitriev
3 years ago

Hi Eugen, thank you for this great article, but I am thinking about this:
You have mentioned that body is null when you extend ResponseEntityExceptionHandler. But in your last implementation with @ControllerAdvice you still extend that class. I am wondering about it, what do you think: is it a good idea to have null body for REST API for those exceptions?? For instance I got HttpMessageNotReadableException when user sent wrong json and I didn’t see the reason in body what was wrong.

Eugen Paraschiv
3 years ago
Reply to  Igor Dmitriev

Hey Igor, I’m glad the article is helpful.
Regarding this base class – it’s a good idea to extend it because it provides some good defaults. But no, most of the time it’s not a good idea to have a null body for your error responses – which is why I’m always calling handleExceptionInternal and passing in a body.
Hope that clears things up. Cheers,
Eugen.

Igor Dmitriev
Igor Dmitriev
3 years ago

I see, you override default behavior if you need. I think it makes sense to improve response message for exceptions. I found these articles useful for me –
1. http://www.baeldung.com/global-error-handler-in-a-spring-rest-api
2. https://blog.jayway.com/2013/02/03/improve-your-spring-rest-api-part-iii/
In general it’s a great idea to improve REST API to help customers.

Comments are closed on this article!