How to Handle an Exception and return custom Error Message Object in Spring Boot using @ControllerAdvice annotation
In this tutorial we are going to learn about handling an exception and returning a custom error Message Object using @ControllerAdvice in Spring Boot.
@ControllerAdvice: Specialization of @Component for classes that declare @ExceptionHandler, /@InitBinder, or @ModelAttribute methods to be shared across multiple @Controller classes.
Classes annotated with @ControllerAdvice can be declared explicitly as Spring beans or auto-detected via classpath scanning. All such beans are sorted based on Ordered semantics or @Order / @Priority declarations, with Ordered semantics taking precedence over @Order / @Priority declarations.
@ControllerAdvice beans are then applied in that order at runtime. Note, however, that @ControllerAdvice beans that implement PriorityOrdered are not given priority over @ControllerAdvice beans that implement Ordered. In addition, Ordered is not honored for scoped @ControllerAdvice beans — for example if such a bean has been configured as a request-scoped or session-scoped bean.
For handling exceptions, an @ExceptionHandler will be picked on the first advice with a matching exception handler method. For model attributes and data binding initialization, @ModelAttribute and @InitBinder methods will follow @ControllerAdvice order.
Let’s have a faulty code snippet in the Rest Controller as shown in the below code snippet.
Sample Code Snippet:
@GetMapping(path = "/{userId}", produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<UserModel> getUser(@PathVariable String userId) {
// Faulty Code
String firtname = null;
int len = firtname.length();
if (user.containsKey(userId)) {
return new ResponseEntity<UserModel>(user.get(userId), HttpStatus.CREATED);
} else {
return new ResponseEntity<UserModel>(HttpStatus.NO_CONTENT);
}
}
Now let’s create a class where we place our @ControllerAdvice annotation where you can have a centralized way to handle exceptions, binding, etc. it applies to all the defined controller.
Sample Code Snippet:
import java.util.Date;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import com.behindjava.tutorial.model.CustomError;
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = { Exception.class })
public ResponseEntity<Object> handleAnyException(Exception ex, WebRequest req) {
String errorMessage = ex.getLocalizedMessage();
if (errorMessage == null)
errorMessage = ex.toString();
CustomError message = new CustomError(new Date(), errorMessage);
return new ResponseEntity<Object>(message, new HttpHeaders(), HttpStatus.UNSUPPORTED_MEDIA_TYPE);
}
}
Now we will create a controller advice that returns a custom error message for the same faulty code placed above in the GET handler mapping and to do so we will create a model class which contains time stamp and message that are passed to the object as we are returning the custom error message with timestamp and message.
Sample Code Snippet:
import java.util.Date;
public class CustomError {
private Date timeStamp;
private String message;
public CustomError() {
}
public CustomError(Date timeStamp, String message) {
this.timeStamp = timeStamp;
this.message = message;
}
public Date getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(Date timeStamp) {
this.timeStamp = timeStamp;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
You can test this by using post man as shown in the below image where you’ll receive the custom error message with timestamp and message.