Spring boot 3 exception handlers with rest controller advice annotation

Nithidol Vacharotayan
4 min readApr 19, 2024

--

Spring Boot provides rest controller advice annotations for exception handlers that reduce redundant code and the typical response format. This helps the development application be easy to maintain. A rest controller advice annotation interceptor is provided for every exception defined in the Java class, assisting the developer in responding to errors in the same format as any request. This makes it easy for the front-end and back-end to communicate.

Scenario

The development team designed a web services application that creates users and needs standard exception handlers when web services are in error.

Discussion

The development team decided to use rest controller advice annotation and design response format for any request that occurs as an exception.

Implementation

The developer creates a Java class and adds a rest controller advice annotation. They assign exceptions that need standard responses and create a typical response Java bean for the standard response format.

For example, rest controller advice annotation

creates restful web services for creating users. return response error when exception or validation error.

Create CommonResponseBean class

import lombok.Getter;
import lombok.Setter;
import java.util.List;

@Getter
@Setter
public class CommonResponseBean {
public long timestamp;
public List<ErrorBean> errors;
}

Create ErrorBean class

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ErrorBean {
public String errorCode;
public String errorMessage;
}

Create UserProfile

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Data
public class UserProfile {

@NotEmpty
private String firstName;

@NotEmpty
private String lastName;

private String effectiveDate;

@Min(0)
private long age;

public UserProfile(String firstName,String lastName, int age, String effectiveDate){
this.firstName = firstName;
this.lastName = lastName;
this.effectiveDate = effectiveDate;
this.age = age;
}
}

Create restful service

import com.example.demo.bean.UserProfile;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.text.ParseException;
import java.text.SimpleDateFormat;

@RestController
public class CreateUser {

@PostMapping(path = "/user", produces = "application/json")
public ResponseEntity<String> create(@Valid @RequestBody UserProfile userProfile) throws ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
if(!userProfile.getEffectiveDate().isEmpty())
df.parse(userProfile.getEffectiveDate());
return new ResponseEntity<>("created",HttpStatus.CREATED);
}
}

Create exception handler

import com.example.demo.bean.CommonResponseBean;
import com.example.demo.bean.ErrorBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;

@RestControllerAdvice
public class CommonExceptionHandlers {

@ExceptionHandler(ParseException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<CommonResponseBean> handleParseException(ParseException ex) {
// Customize the response entity
CommonResponseBean res = new CommonResponseBean();
res.setTimestamp(new Date().getTime());
res.setErrors(new ArrayList<>());
res.getErrors().add(new ErrorBean("E0001",ex.getMessage()));
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(res);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<CommonResponseBean> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
// Customize the response entity
CommonResponseBean res = new CommonResponseBean();
res.setTimestamp(new Date().getTime());
res.setErrors(new ArrayList<>());
res.getErrors().add(new ErrorBean("E0002",ex.getMessage()));
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(res);
}
}

Test case scenario

Using Postman is an easy tool for creating test case scenarios. this suite test case will show how it works rest controller advice annotation.

Test case: call web services path /user

Test result: response body from web services is “created”, and HTTP status 201 is created.

Test case: call web services path /user, and first name is empty

Test result: The response body from web services is an error, and HTTP status 400 is a bad request.

This response contains timestamps and errors. This format is defined in “CommonResponseBean” when MethodArgumentNotValidException occurs. the developer can modify the error message from this exception can readable.

Test case: call web services path /user, and effective date invalid pattern

Test result: The response body from web services is an error, and HTTP status 400 is a bad request.

This response contains timestamps and errors. This is the same format as MethodArgumentNotValidException, defined in “CommonResponseBean” when ParseException occurs.

In conclusion, this test scenario will show the result that any exceptions put in “CommonExceptionHandlers” use a custom response error that the developer has designed. This design made the exception handler easy to maintain.

Finally, Rest Controller advice annotation made RESTful Web Services
reduce redundancy code and easy to maintain because all exceptions are contained in a single class. the developer can add various exceptions in this class and modify an error response format and error message for any exception. It is friendly for the front-end to receive a response that all an error is the same format. the front-end developer can easily organize error message formats that are displayed to clients.

Thank you for reading.

--

--

Nithidol Vacharotayan

Programming enthusiast with 10+ years of experience in Java loves sharing knowledge with others and exploring new technologies together!