Skip to content

Spring Framework Blog

Menu
  • Home
  • Spring
  • Spring Boot
  • About
Menu

Spring Exception Handler

Posted on January 30, 2024June 4, 2024 by Armando Marques

Error Handling for REST with Spring

Before Spring 3.2 the two main approaches to handle exceptions where HandleExceptionResolver or @ExceptionHandler annotation. The it was added @ControllerAdvice and ResponseStatusException.

Table of Contents

Toggle
  • Controller level (MVC)
  • HandleExceptionResolver
    • DefaultHandlerExceptionResolver
    • ResponseStatusExceptionResolver
    • Custom HadlerExceptionResolver
  • ControllerAdvice and ExceptionHandler
  • ResponseStatusException
  • More details
    • Using HTTP Status Codes
    • Controller Based Exception Handling
      • Using @ExceptionHandler

Controller level (MVC)

This strategy only works for active controllers is not global to the entire application.

@ExceptionHandler({Exception.class,Exception1.class})
public void handleException(){
//handle
}

HandleExceptionResolver

This is enabled by default in DispatchServlet.

DefaultHandlerExceptionResolver

Used to resolve Spring exceptions to their corresponding HTTP Status codes

Handling Standard Spring MVC Exceptions

ResponseStatusExceptionResolver

Uses @ResponseStatus annotation on custom exceptions to map them into HTTP Status codes.

However this does not allow to set body content.

@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);
    }
}

Custom HadlerExceptionResolver

Creates an extension of AbstractHandlerExceptionResolver that allows to set the body content.

@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();
    }
}

ControllerAdvice and ExceptionHandler

More suitable for Rest, this solution breaks from the MVC model allows to return an ResponseEntity object using @ExceptionHandler annotation. @ControllerAdvice allows to set the handler as common to all controllers and consolidate our multiple, scattered @ExceptionHandlers from before into a single, global error handling component.

@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);
    }
}

ResponseStatusException

Allow to throw an exception of type ResponseStatusException with an Http status and optional reason and clause, a more flexible but less methodical approach.

@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);
    }
}

More details

Using HTTP Status Codes

Normally any unhandled exception thrown when processing a web-request causes the server to return an HTTP 500 response. However, any exception that you write yourself can be annotated with the @ResponseStatus annotation (which supports all the HTTP status codes defined by the HTTP specification). When an annotated exception is thrown from a controller method, and not handled elsewhere, it will automatically cause the appropriate HTTP response to be returned with the specified status-code.

For example, here is an exception for a missing order.

COPY @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order")  // 404
 public class OrderNotFoundException extends RuntimeException {
     // ...
 }

And here is a controller method using it:

COPY @RequestMapping(value="/orders/{id}", method=GET)
 public String showOrder(@PathVariable("id") long id, Model model) {
     Order order = orderRepository.findOrderById(id);

     if (order == null) throw new OrderNotFoundException(id);

     model.addAttribute(order);
     return "orderDetail";
 }

A familiar HTTP 404 response will be returned if the URL handled by this method includes an unknown order id.

Controller Based Exception Handling

Using @ExceptionHandler

You can add extra (@ExceptionHandler) methods to any controller to specifically handle exceptions thrown by request handling (@RequestMapping) methods in the same controller. Such methods can:

  1. Handle exceptions without the @ResponseStatus annotation (typically predefined exceptions that you didn’t write)
  2. Redirect the user to a dedicated error view
  3. Build a totally custom error response

The following controller demonstrates these three options:

COPY@Controller
public class ExceptionHandlingController {

  // @RequestHandler methods
  ...

  // Exception handling methods

  // Convert a predefined exception to an HTTP Status code
  @ResponseStatus(value=HttpStatus.CONFLICT,
                  reason="Data integrity violation")  // 409
  @ExceptionHandler(DataIntegrityViolationException.class)
  public void conflict() {
    // Nothing to do
  }

  // Specify name of a specific view that will be used to display the error:
  @ExceptionHandler({SQLException.class,DataAccessException.class})
  public String databaseError() {
    // Nothing to do.  Returns the logical view name of an error page, passed
    // to the view-resolver(s) in usual way.
    // Note that the exception is NOT available to this view (it is not added
    // to the model) but see "Extending ExceptionHandlerExceptionResolver"
    // below.
    return "databaseError";
  }

  // Total control - setup a model and return the view name yourself. Or
  // consider subclassing ExceptionHandlerExceptionResolver (see below).
  @ExceptionHandler(Exception.class)
  public ModelAndView handleError(HttpServletRequest req, Exception ex) {
    logger.error("Request: " + req.getRequestURL() + " raised " + ex);

    ModelAndView mav = new ModelAndView();
    mav.addObject("exception", ex);
    mav.addObject("url", req.getRequestURL());
    mav.setViewName("error");
    return mav;
  }
}

In any of these methods you might choose to do additional processing – the most common example is to log the exception.

Handler methods have flexible signatures so you can pass in obvious servlet-related objects such as HttpServletRequest, HttpServletResponse, HttpSession and/or Principle.

Important Note: The Model may not be a parameter of any @ExceptionHandler method. Instead, setup a model inside the method using a ModelAndView as shown by handleError() above.

  • Spring
  • Documentation
  • References
  • Toc
  • Books
  • Certification
  • AOP
  • Config
  • Java
  • Java core
  • JDBC
  • JPA
  • Rest
  • Security
  • Spring
  • Spring Boot
  • Spring Core
  • Spring Data
  • Spring MVC
  • Spring Rest
  • Spring Security
  • Tests
  • Transactions
  • Uncategorized

Recent Posts

  • Spring Annotations
  • Java Tests
  • Java operators
  • Java versions
  • Java Oracle Licenses
  • Configuration properties
  • MockMvc
  • Spring Security III
  • MVC Controller Method Params
  • JPA Methods
  • Transaction propagation and isolation
  • Spring JDBC
  • Spring Boot Auto-Configuration
  • Spring Resource interface
  • JSR 330 Standard Annotations
  • Spring Aware Interfaces
  • Spring Transactions
  • Spring Boot Core
  • MVC Rest
  • Spring Boot JPA
©2025 Spring Framework Blog | Built using WordPress and Responsive Blogily theme by Superb