This post will illustrate a way in which exception handling can be implemented for RESTful web services in Spring in such a manner that the exception handling concerns are separated from the application logic.
Taking advantage of the @ControllerAdvice
annotation we are able to create a global helper class for all controllers. By adding methods that we annotate with both @ExceptionHandler
and @ResponseStatus
we can specify which type of exception gets mapped to which HTTP response status. E.g., our custom NotFoundException
can be mapped to a HTTP response of 404 Not Found, or all exceptions that are not caught elsewhere will result in HTTP status 500 Internal Server Error by catching java.lang.Exception
, or an IllegalArgumentException
can result in 400 Bad Request, or… well, I’m sure you got the general idea.
If required, you can also send back some more detail to the client by adding @ResponseBody
into the mix.
Below is a very limited example to get you started.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package it.jdev.examples.spring.rest.exceptions; import java.lang.invoke.MethodHandles; import org.slf4j.*; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.ServletWebRequest; @ControllerAdvice @Order(Ordered.LOWEST_PRECEDENCE) public class GeneralRestExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler(CustomNotFoundException.class) public void handleNotFoundException(final Exception exception) { logException(exception); } @ResponseStatus(HttpStatus.FORBIDDEN) @ExceptionHandler(CustomForbiddenException.class) public void handleForbiddenException(final Exception exception) { logException(exception); } @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler({ CustomException.class, Exception.class }) public void handleGeneralException(final Exception exception) { logException(exception); } @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) public void handleGeneralException(final Exception exception) { logException(exception); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler({ CustomBadRequestException.class, IllegalArgumentException.class }) @ResponseBody public String handleBadRequestException(final Exception exception) { logException(exception); return exception.getMessage(); } // Add more exception handling as needed… // … private void logException(final Exception exception) { // ... } } |