How to Customize Default Error Message using @ControllerAdvice in Spring Boot Validation


The annotation @Valid is used to validate the spring boot bean. The validation shows the default error message to the client. The generic error message must be customized to the application. All the error messages must be handled in a single place. The @ControllerAdvice annotation is used to handle all the validation errors in a single place.

The default error message is a generic error message. The annotation @ControllerAdvice helps to customize the default error message to a user-based error message.

In this post, we will see how to customize the default error message using @ControllerAdvice. Customization is done in one place. This example helps you understand how to ignore a @valid error message, how to convert a default @valid error message to a custom error message, how to convert a default @valid error message to a reply message.



Pom.xml file and dependency

The pom.xml file contains all the configurations of the dependency. This dependency includes all the jar files required for validation.

The “spring-boot-starter-validation” dependency will include all the jars needed for the validation of the request body. The web application needs the dependence of “spring-boot-starter-browser.” This will allow the tomcat server and start as a web application. The default “spring-boot-starter-test” test dependency will be applied to pom.xml for the unit testing.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.5.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.yawintutor</groupId>
	<artifactId>Spring-Application</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringBootValidation</name>
	<description>Spring Boot Project</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>


Spring Boot Bean class to validate

The spring boot bean class is required to store the value obtained from the client or browser in json format. A spring boot bean class contains private variables and getter and setter method. The bean class has annotation for validating the data. In this example the annotation @Min is used to validate a student age.

Student.java

package com.yawintutor;

import javax.validation.constraints.Min;

public class Student {

	@Min(5)
	private int age;

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}


Controller Class to call the api

The code below shows the test controller class that contains a post method. The @ControllerAdvice code is invoked automatically when the post method is called.

TestController.java

package com.yawintutor;

import javax.validation.Valid;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
public class TestController {
	
	  @PostMapping("/student/")
	  ResponseEntity<String> student(@Valid @RequestBody Student student) {
		    return ResponseEntity.ok("Your age is " + student.getAge());
	  }
}


@ControllerAdvice class to handle error message

The Controller Advice class uses the @ControllerAdvice annotation. This @ControllerAdvice class is notified when a validation error occurs in the controller class. The code below shows how to handle validation errors in the @ControllerAdvice class.

MethodArgumentNotValidExceptionHandler.java

package com.yawintutor;

import java.util.ArrayList;
import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class MethodArgumentNotValidExceptionHandler {
	
    @ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public Error handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, WebRequest request) {
        BindingResult result = ex.getBindingResult();
        
        List<String> errorList = new ArrayList<>();
        result.getFieldErrors().forEach((fieldError) -> {
        	errorList.add(fieldError.getObjectName()+"."+fieldError.getField()+" : " +fieldError.getDefaultMessage() +" : rejected value [" +fieldError.getRejectedValue() +"]" );
        });
        result.getGlobalErrors().forEach((fieldError) -> {
        	errorList.add(fieldError.getObjectName()+" : " +fieldError.getDefaultMessage() );
        });
        
        return new Error(HttpStatus.BAD_REQUEST, ex.getMessage(), errorList);
    }

    public static class Error{
    	private int errorCode;
    	private String error;
    	private String errorMessage;
    	private List<String> fieldErrors = new ArrayList<>();
    	
    	public Error(HttpStatus status, String message, List<String> fieldErrors ) {
    		this.errorCode = status.value();
    		this.error = status.name();
    		this.errorMessage = message;
    		this.fieldErrors = fieldErrors;
    	}

		public int getErrorCode() {
			return errorCode;
		}

		public void setErrorCode(int errorCode) {
			this.errorCode = errorCode;
		}

		public String getError() {
			return error;
		}

		public void setError(String error) {
			this.error = error;
		}

		public String getErrorMessage() {
			return errorMessage;
		}

		public void setErrorMessage(String errorMessage) {
			this.errorMessage = errorMessage;
		}

		public List<String> getFieldErrors() {
			return fieldErrors;
		}

		public void setFieldErrors(List<String> fieldErrors) {
			this.fieldErrors = fieldErrors;
		}
    }
}


Test the validation error

To test the validation error, run the following curl command in the command prompt or import it to the postman tool and run it in the postman tool.

Curl Command

curl -X POST \
  http://localhost:8080/student/ \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -H 'Postman-Token: a3a5d66e-2b98-4b7d-8400-9be137b01504' \
  -d '{"age":"3"}'

Output

{
    "errorCode": 400,
    "error": "BAD_REQUEST",
    "errorMessage": "Validation failed for argument [0] in org.springframework.http.ResponseEntity<java.lang.String> com.yawintutor.TestController.student(com.yawintutor.Student): [Field error in object 'student' on field 'age': rejected value [3]; codes [Min.student.age,Min.age,Min.int,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.age,age]; arguments []; default message [age],5]; default message [must be greater than or equal to 5]] ",
    "fieldErrors": [
        "student.age : must be greater than or equal to 5 : rejected value [3]"
    ]
}



Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *