Sunday, 5 May 2024

How to Validate Lists in Spring Boot Requests


In Spring Boot, validating incoming data in API requests is crucial for maintaining data integrity and ensuring that only valid data is processed by your application. In this blog post, we’ll explore various methods to validate lists of strings and other types of data, specifically focusing on ensuring that each element in a list adheres to specified validation rules. We will look at examples using the @Valid annotation and delve into custom validators for more complex scenarios.

1. Validating Email Lists in a Request DTO

When dealing with lists, such as a list of emails, it’s important to ensure that each email address is valid. Spring Boot’s validation framework simplifies this by allowing annotations to be applied directly to the elements of a collection. Here’s an example:

import javax.validation.constraints.Email;
import javax.validation.constraints.Size;
import java.util.List;

public class DataRequest {
    @Size(min=1, max=10)
    private String dataTitle;

    private List<@Email String> emails; // Ensures each email in the list is valid
}

In the example above, the @Email annotation is used to validate that each string in the emails list is a valid email address. This approach can be extended to other patterns using the @Pattern annotation:

import javax.validation.constraints.Pattern;

public class DataRequest {
    @Size(min=1, max=10)
    private String dataTitle;

    private List<@Pattern(regexp = "[A-Za-z\\d]+@[A-Za-z\\d]+\\.com") String> emails;
}

This configuration ensures each string in the list matches the specified regular expression, ideal for custom email domain validation.

2. Using Custom Validators for Complex List Validations

Sometimes, standard annotations might not suffice, especially when complex validation logic is required. For these scenarios, defining a custom validation annotation is the way to go. Here’s how you can implement a custom validator for a list:

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = ListNotEmptyValidator.class)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RUNTIME)
public @interface ListNotEmpty {
    String message() default "List cannot be empty";

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

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

public class ListNotEmptyValidator implements ConstraintValidator<ListNotEmpty, List<?>> {
    @Override
    public boolean isValid(List<?> value, ConstraintValidatorContext context) {
        return value != null && not empty;
    }
}

In the custom annotation @ListNotEmpty, the isValid method checks if the list is neither null nor empty. This annotation can be used in your DTO to ensure a list is not submitted empty:

public class RecipeForm {
    @ListNotEmpty(message = "Step list cannot be empty")
    private List<String> steps;
}

3. Validating Nested Objects in a List

For scenarios where each element in a list is a complex object that requires multiple field validations, Spring Boot handles this seamlessly with nested validation:

import javax.validation.Valid;
import java.util.List;

public class RecipeForm {
    @Valid
    private List<Ingredient> ingredients;
}

public class Ingredient {
    @NotBlank(message = "Ingredient name is required")
    private String name;

    @NotNull(message = "Quantity is required")
    private Integer quantity;
}

Using @Valid on the list of Ingredient objects ensures that each ingredient adheres to its own validation rules.

Spring Boot offers powerful and flexible validation mechanisms that can be tailored to fit virtually any data validation need. Whether you’re validating simple data types, lists, or complex nested objects, using built-in annotations or creating custom validators provides robust control over the data your application processes. Always ensure that your validation logic is as precise as necessary to protect your application from invalid data while providing clear feedback to the users or systems interacting with your APIs.

Labels:

0 Comments:

Post a Comment

Note: only a member of this blog may post a comment.

<< Home