Custom Annotation In Spring Boot

About this article, most people find it on a basic level, but the impact of this is more useful when it comes to dealing with input fields. So let’s see what we can learn in this article.

Generally, when we need to validate user input or form fields, Spring offers standard predefined validators and those we are using already. However, when we need to validate a more particular type of input, we have the possibility of creating our own, custom validation logic.

In this article, we’ll do just that – we’ll create a custom validator to validate a form with an email, password, name, and phone number field.

Project Setup

  • We are going to use the Spring boot project so just create one spring boot project using IDE or you can simply create from https://start.spring.io/
  • Once the project is created add below maven dependency
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <optional>true</optional>
</dependency>
  1. Validation
    -Spring Boot’s Bean Validation support comes with the validation starter.
    Note that the validation starter does no more than adding a dependency to a compatible version of hibernate validator, which is the most widely used implementation of the Bean Validation specification.
  2. Common lang3
    -This will need to StringUtils
  3. Lombok
    We want to avoid writing getter and setter arguments.

Custom Validation

Creating a custom validator entails us rolling out our annotation and using it in our model to enforce the validation rules.

So we are going to create four validation listed below,

  • Email: Email should be valid
  • Name: Name does not contain numbers and special character
  • Contact No: Should contain the only number
  • Password: Password should contain at least one number, one special symbol, one upper case, one lower case letter, and length of 5 to 20 char

We need to create a new @interface to define our annotation

@Documented
@Retention(RUNTIME)
@Target({ FIELD, ANNOTATION_TYPE, TYPE_USE })
@Constraint(validatedBy = EmailValidator.class)
public @interface ValidEmail {
 String message() default "{email.invalid}";

 Class<?>[] groups() default {};

 Class<? extends Payload>[] payload() default {};
}

With the use of @Constraint annotation, we can define our class that is going to validate our field, the message() is the error message that is going to be shown in the response and the rest code is the most boilerplate code of spring standards.

Creating Validator

Let’s create a validator - We will call it as an EmailValidator

public class EmailValidator implements ConstraintValidator<ValidEmail, String> {
 private String regex = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
 + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";

 @Override
 public boolean isValid(String email, ConstraintValidatorContext context) {
 if (StringUtils.isBlank(email))
 return true;

 Pattern pat = Pattern.compile(regex);
 return pat.matcher(email).matches();
 }

}

Now if you see, we have used the same regex which we usually use on the frontend side.

The validation class implements the ConstraintValidator interface and must implement the isValid method; it’s in this method that we defined our validation rules.ConstraintValidator defines the logic to validate a given constraint for a given object.

Applying validation annotation
We have looked for EmailValidator so let’s use email validator annotation to email field

@NotBlank
@ValidEmail
private String email;

@NotBlank – This is provided by spring only to check whether the field is blank or not.
@ValidEmail – If you look in the above code we have ConstraintValidator as ValidEmail, So ultimately the ValidEmail will be our custom annotation.

Create Controller

Now we are going to create one controller to test our annotation, we will create a user controller in that the rest endpoint will be /user/validate

@RestController
@RequestMapping("user")
public class UserController {

 @Autowired
 private ResponseMaker responseMaker;


 @PostMapping("/validate")
 public ResponseEntity<SuccessResponse<String>> validateObject(@RequestBody @Valid UserModel userModel) {
 return responseMaker.successResponse("User validated successfully", HttpStatus.OK);
 }

}

Now in the controller one thing we need to make sure that we have used @Valid annotation to our Model class (In which we have added our validation annotation)

Other Configuration

We have added a few exception handlers so that we can format our success and error responses, these are generic exception handlers which can be easily available on the internet.

  • CustomException – This extends the RuntimeException class.
  • ErrorCode – We have added a few error codes which can be produced while running the application and for invalid input, we will use the Bad Request(400) error code.
  • GlobleExceptionHandler – This extends ResponseEntityExceptionHandler and overrides the exceptions.
  • ValidatioErrorResponse – To customize the error message
  • ResponseMaker – To create the final response which is used in the controller.
  • SuccessResponse – To create a successful response.
  • ErrorResponse – To create an error response.
  • ValidationMessages.properties – The properties file to keep our error messages in one place.

Running the project

Let’s check a few scenarios to check our validations

  1. Keep blank all the fields,
Request:
{

}

Response:
{
    "message": "Please enter valid data",
    "code": 400,
    "error": true,
    "validationErros": [
        {
            "fieldName": "contactNo",
            "message": "must not be blank"
        },
        {
            "fieldName": "name",
            "message": "must not be blank"
        },
        {
            "fieldName": "email",
            "message": "must not be blank"
        },
        {
            "fieldName": "password",
            "message": "must not be blank"
        }
    ]
}

2. Invalid user input request,

Request:

{
 "email":"asdlj",
 "password":"hasd",
 "name":"asdas343",
 "contactNo":"asdlsdlkj"
}

Response:

{
    "message": "Please enter valid data",
    "code": 400,
    "error": true,
    "validationErros": [
        {
            "fieldName": "password",
            "message": "Password should contain at least one number, one special symbol,one upper case, one lower case letter and length of 5 to 20 char"
        },
        {
            "fieldName": "email",
            "message": "Email is invalid."
        },
        {
            "fieldName": "contactNo",
            "message": "Mobile number is invalid."
        },
        {
            "fieldName": "name",
            "message": "Numbers & Special Characters are not allowed."
        }
    ]
}

   3. Success request,

Request:

{
 "email":"dg12kumbhar@gmail.com",
 "password":"Password@123",
 "name":"Deepak",
 "contactNo":"9876543210"
}

Response:
{
"code": 200,
"data": "User validated successfully"
}
coma

Note:
The first validation is handled by @NotBlank annotation, which is provided by Spring only.

Yes, that’s it. We have successfully created our custom annotation and integrated it with an application.

You can find this complete repository here GitHub Link.

Thank you for reading this article, If you have any doubts, the comment section is always open.

Keep Reading

Keep Reading

  • Service
  • Career
  • Let's create something together!

  • We’re looking for the best. Are you in?