In this article, we focus on exception handling for a REST API.
We talk about the various techniques to implement effective error handling for the Spring Boot REST APIs.
Time needed: 10 minutes.
What you will learn?
- What is the exception/error handling?
- Why exception handling is important?
- What are the APIs and annotations provided by spring boot for exception handling?
- How to handle exception globally across the entire application?
- Things you should consider while designing exception handling for REST APIs.
Introduction to Exception handling for REST APIs
In the typical Spring Boot REST-based application, we generally expose several REST endpoints for consumers to communicate with the server.
Those consumers could be a third-party mobile application or some other web application.
However, third-party developers may not have an understanding of the REST server.
Henceforth, while designing the REST-based system, the developer should think about both positive and negative scenarios.
Thus, a good REST implementation should have good error handling coverage throughout the application.
Error handling gives us the flexibility to decide what to return to the client for each type of exception.
Before going further to create a demo, let’s understand what is @RestControllerAdvice annotation and how it helps in handling exception across the entire application.
@RestControllerAdvice
The @ControllerAdvice and @RestControllerAdvice annotations allow us to add exception handling for the entire application.
However, for Spring Boot REST exception handling, we will use @RestControllerAdvice
, which is nothing but a combination of @ControllerAdvice
and @ResponseBody
@ControllerAdvice or @RestControllerAdvice ensures that any unhandled exception that is thrown anyone in the entire application will come over here.
Therefore, you can handle all exceptions in one place centrally.
To demonstrate @RestControllerAdvice
usage, let’s create a class GlobalExceptionHandler
and define all exceptions that you want to handle for entire application.
package com.codedelay.rest.exception; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { }
As you could see in the above example, GlobalExceptionHandler
is annotated with @RestControllerAdvice
that gives the guarantee that any handled exception thrown throughout the program will be routed here.
Now let’s define a method handleRuntimeException
to handle runtime exceptions for the entire app.
If you notice the below program, then you can observe that handleRuntimeException
is annotated with @ExceptionHandler
.
@ExceptionHandler
allows you to define a method that can handle the exception.
package com.codedelay.rest.exception; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice(basePackages = {"com.codedelay.rest.controller"}) public class GlobalExceptionHandler { @ExceptionHandler(RuntimeException.class) public String handleRuntimeException(){ return "Some error occurred"; } }
Whenever RuntimeException exception occurs in the application, then exceptionHandler() method will be called and Some error occurred
message will be returned.
Let’s change the controller logic to throw RunTimeException.
package com.codedelay.rest.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.codedelay.rest.exception.UserNameNotPresentException; @RestController @RequestMapping("/api") public class WelcomeController { @GetMapping("/welcome/{userName}") public String welcomeMsg(@PathVariable("userName") String userName) throws UserNameNotPresentException { return "Welcome" + " " + userName; } @GetMapping("/welcome") public String welcomeMsg() throws UserNameNotPresentException { throw new UserNameNotPresentException("please provide user name in the URL"); } @GetMapping("/") public void handleRequest() { throw new RuntimeException(); } }
The WelcomeController will throw RuntimeException if /api/
called.

How to handle exceptions and return a standard JSON
Steps to handle exceptions and return a standard JSON
- Add a POJO class (ErrorInfo) class that will represent the JSON structure.
- Create global
@RestControllerAdvice
exception handler.
Add an error info class that will represent the JSON structure.
For this demo, we will show the below JSON message when an error occurs.
{ "message":"java.lang.RuntimeException", "timestamp":"2019-08-02T07:12:50.524+0000", "uri":"/api/" }
From the above JSON, you could see that JSON message has three properties i.e. error message, timestamp, and URI.
Let’s try to represent the above JSON message in a Java class (ErrorInfo).
package com.codedelay.rest.exception; import java.util.Date; import com.fasterxml.jackson.annotation.JsonProperty; public class ErrorInfo { @JsonProperty private String message; @JsonProperty("timestamp") private Date timestamp; @JsonProperty("uri") private String uriRequested; public ErrorInfo(Exception exception, String uriRequested) { this.message = exception.toString(); this.uriRequested = uriRequested; this.timestamp = new Date(); } public String getMessage() { return message; } public Date getTimestamp() { return timestamp; } public String getUriRequested() { return uriRequested; } }
Create a global @RestControllerAdvice
exception handler.
@RestControllerAdvice
is a better way of handling exceptions.
GlobalExceptionHandler
is a centralized place that will handle all applications exception.
package com.codedelay.rest.exception; import javax.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice(basePackages = { "com.codedelay.rest.controller" }) public class GlobalExceptionHandler { private ResponseEntity<ErrorInfo> error(final Exception exception, final HttpStatus httpStatus, HttpServletRequest request) { return new ResponseEntity<>(new ErrorInfo(exception, request.getRequestURI()), httpStatus); } @ExceptionHandler(RuntimeException.class) public ResponseEntity<ErrorInfo> handleRuntimeException(HttpServletRequest request, final RuntimeException e) { return error(e, HttpStatus.NOT_FOUND, request); } @ExceptionHandler(UserNameNotPresentException.class) public ResponseEntity<ErrorInfo> handleUserNotPresentException(HttpServletRequest request, UserNameNotPresentException e) { return error(e, HttpStatus.BAD_REQUEST, request); } }
GlobalExceptionHandler is handling two exceptions globally.
handleRuntimeException
is handling all RuntimeExceptions
.
And in case if RuntimeExceptions
has thrown by the controller, handleRuntimeException
will return a JSON message that contains an error message, the timestamp when the exception occurred, and HttpStatus.NOT_FOUND
error.

handleUserNotPresentException
is handling a custom exception UserNameNotPresentException
.
package com.codedelay.rest.exception; public class UserNameNotPresentException extends Exception { /** * */ private static final long serialVersionUID = 1L; public UserNameNotPresentException(String message) { super(message); } }
In case if UserNameNotPresentException
has thrown by the controller (WelcomeController
), handleUserNotPresentException
will return a JSON message that contains an error message, the timestamp when the exception occurred, and HttpStatus.BAD_REQUEST
error.

Conclusion
In this Spring Boot Rest Service Exception Handling tutorial, we have seen how to use @RestControllerAdvice
to handle all exceptions centrally.
Great article Arpit!