API validation in SpringBoot

API validation in SpringBoot

In today’s digital landscape, applications handle vast amounts of data that require thorough validation and error checking before being processed and stored in databases. Additionally, with numerous API integrations, ensuring data integrity should be a top priority for developers. One approach to data validation is checking all fields one by one and by using if/else that will generate a bad smell! However, this method often leads to cluttered code, making it difficult to read and maintain. Furthermore, such code is typically not reusable across different parts of an application or in various projects. As applications scale, the complexity of validation rules increases, making it challenging and error-prone to manage intricate validation logic through if/else statements.

In Spring Boot, validation annotations are utilized to ensure that data is matched to specified constraints before processing. By including the spring-boot-starter-validation dependency, we can leverage the Java Bean Validation API, which integrates seamlessly with Spring. Common annotations such as @NotNull, @Size, and @Email can be applied directly to fields in model (or DTO - Data Transfer Object) classes to enforce rules like non-nullability and format checks. To activate validation, methods can be annotated with @Validated, allowing Spring to automatically validate input parameters and return appropriate error messages if validation fails, also @Valid annotation is used to enable validation of method parameters and return values. When applied to a method parameter, Spring will automatically validate the parameter using the configured validation constraints thereby enhancing data integrity and application reliability.

Let's take an example for learning it. We have a micro-service for managing system's users. This service has only one API and actually it is for creating new users. Nobody can exit from this system after signing up once :)


Article content
CreateUserRequest.java



Article content
CreateUserController.java

CreateUserRequest class is a DTO and used for transferring data from client to server and contains four fields and CreateUserController is a controller for creating new users. We added a @Valid before createUserRequest parameter because we want to check and validate data.

After calling API and before anything the system validates the body of request (I mean createUserRequest). If name and surname are null or are empty it will throw an exception for them that tells the client about invalid parameters.

Additionally, we can create my own annotations for more complex validation. For example we want to accept only ages between 18 to 65 and also it is a mandatory field. Instead of using 3 annotations for age fields we can wrap all of them in one annotation for a cleaner code.


Article content
ValidAge.java

The @ValidAge annotation is a custom validation annotation in and it is implemented to validate that an age value is mandatory and falls within a specified range, specifically between 18 and 65 years.

Components of the @ValidAge

  • @Constraint(validatedBy = {}):This annotation indicates that the @ValidAge annotation is a constraint. The validatedBy attribute is an array of classes that implement the ConstraintValidator interface. In this case, it's empty ({}), which implies that the validation logic is handled by the other annotations (@NotNull, @Min, @Max) rather than a custom validator.
  • @NotNull:This annotation ensures that the annotated field cannot be null. If the field is null, a validation error will occur, indicating that a value must be provided.
  • @Min(18):This annotation specifies that the annotated field must have a value of at least 18. If the age is less than 18, a validation error will be triggered.
  • @Max(65):Similar to @Min, this annotation sets an upper limit for the annotated field, specifying that the value must not exceed 65. If the age is greater than 65, a validation error will occur.
  • @Target({ElementType.FIELD}):This annotation defines the applicable target for the @ValidAge annotation. In this case, it can only be applied to fields (i.e., instance variables of a class).
  • @Retention(RetentionPolicy.RUNTIME):This annotation specifies how long the annotation should be retained. RUNTIME means that the annotation will be available at runtime, allowing it to be accessed via reflection.
  • Message: This element allows you to specify a custom error message that will be returned if validation fails. The default message is "INVALID_AGE_EXCEPTION".

For a more complex example we want to accept only valid email and the email must be unique in the system.


Article content
UniqueEmail.java


Article content
UniqueEmailValidator.java

The UniqueEmailValidator class is a custom constraint validator implementation. It is used to validate that an email address is unique within the system.

By implementing the ConstraintValidator interface we should override the isValid method. The return value of this method is a boolean and where it is True it means the value is valid and when it is False it the value is not valid. In the code below we will use this new validation annotation in our code.


Article content
ValidEmail.java

For viewing the complete version of code and testing different situations please check this link.

Thank you, Hassan Golshani

To view or add a comment, sign in

More articles by Hassan Golshani

Others also viewed

Explore content categories