How to put validation on list of String coming from request in spring boot?

1k Views Asked by At

I am creating Spring boot APIs and one of the API consumening data below:

class DataRequest{

    @Size(min=1, max=10)
    private String dataTitle;

    private List<String> emails;

}

How can we validate List like all strings must be valid emails or matching with some pattern by utilizing a validation framework in Spring controller using @Valid annotation?

3

There are 3 best solutions below

0
Ken Chan On BEST ANSWER

Bean validation allows you to put the validating annotation inside the container type such as List. They refer this as the container element constraint validation.

So you can do something likes :

class DataRequest{

    @Size(min=1, max=10)
    private String dataTitle;

    private List<@Email String> emails;

}

or

class DataRequest{

    @Size(min=1, max=10)
    private String dataTitle;

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

}
0
ash On

In fact ,if you want to control the size of list ,you may use the @Size on you parameter, some @valid annotation has support List.
But if you want to make some complicated function, i suggest you implement your own validation annotation by the support of @Valid.
Follow i will show you a simple implement:

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

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

/**
 * @author yujin
 */
@Documented
@Constraint(validatedBy = {Show.ShowConstraintValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface Show {
    String message() default "{com.annotation.Show.message}";

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

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

    int[] value();

    class ShowConstraintValidator implements ConstraintValidator<Show, Integer> {

        private Set<Integer> set = new HashSet<>();

        @Override
        public void initialize(Show constraintAnnotation) {
            int[] value = constraintAnnotation.value();
            for (int i : value) {
                set.add(i);
            }
        }



        @Override
        public boolean isValid(Integer value, ConstraintValidatorContext context) {
            return set.contains(value);
        }
    }
}

The dependency is :

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
0
Kevin O. On

Multiple Validators to validate the objects in a List (using Thymeleaf)

FormBean.java

@Data
@NoArgsConstructor
public class RecipeForm {

    @NotBlank(message = "{error.general.not-blank}")
    @Size(min = 8, message = "{error.recipe.name.min}")
    @Size(min = 32, message = "{error.recipe.name.max}")
    private String name;

    @NotBlank(message = "{error.general.not-blank}")
    private String image;

    @NotNull(message = "{error.general.not-null}")
    @Min(value = 1, message = "{error.recipe.category}")
    @Max(value = 3, message = "{error.recipe.category}")
    private Long categoryId;

    @NotNull(message = "{error.general.not-null}")
    private List<
            @NotBlank(message = "{error.general.not-blank}")
            @Size(min = 16, message = "{error.recipe.step.min}")
            @Size(max = 256, message = "{error.recipe.step.max}") String> steps = new ArrayList<>(List.of("", "", "", ""));
}

ValidationMessages_en.properties

error.general.not-null = should not be null
error.general.not-blank = is required
error.recipe.step.min = number of characters not correct: at least {min} characters
error.recipe.step.max = number of characters not correct: maximum {max} characters

view.html

<!--/* @thymesVar id="myForm" type="com.example.homepage.form.MyForm" */-->
<form novalidate th:action="@{/postEndpointUrl}" th:object="${myForm}" method="post">
    <div class="col" th:each="step, stat : *{steps}">
       <div class="form-outline">
          <textarea th:id="|textarea-step-${stat.index}|" class="form-control" rows="3" th:field="*{steps[__${stat.index}__]}" minlength="16" maxlength="256" required></textarea>
          <label th:for="|textarea-step-${stat.index}|" class="form-label" th:text="${stat.index}"></label>
       </div>
       <div class="text-start text-danger" th:if="${#fields.hasErrors('steps[' + stat.index + ']')}">
          <ul class="ps-0 mb-0 small">
             <li th:each="err : ${#fields.errors('steps[' + stat.index + ']')}" th:text="${err}"></li>
          </ul>
       </div>
    </div>
</div>