Spring Boot 如何在Java列表类型上创建自定义验证器?

dl5txlt9  于 2022-11-05  发布在  Spring
关注(0)|答案(3)|浏览(192)

我有一个NumberConstraint,如下所示:

@Constraint(validatedBy = { StringConstraintValidator.class, })
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, })
public @interface StringConstraint {

    String message() default "'${validatedValue}' ist not valid Number. " +
            "A String is composed of 7 characters (digits and capital letters). Valid example: WBAVD13.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

并由StringConstraintValidator进行验证,如下所示:

@Component
public class StringConstraintValidator implements ConstraintValidator<StringConstraint, String> {

    private static final String REGEX_String = "^[A-Z0-9]{7}$";

    @Override
    public void initialize(final StringConstraint annotation) {
        // noop
    }

    @Override
    public boolean isValid(final String value, final ConstraintValidatorContext context) {
        return isValid(value);
    }

    public boolean isValid(final String value) {
        // Number is not always null Checked via @NotNull
        LoggingContext.get().setString(value);
        if (value == null) {
            return false;
        }
        return Pattern.compile(REGEX_STRING).matcher(value).matches();
    }
}

我可以将此StringConstraint应用于请求对象的单个字段,如下所示:

@StringConstraint
private String number;

但是如果我的请求对象包含一个字符串列表,那么我如何在整个列表上使用这个约束,或者我必须在列表类型上定义一个新的约束?类似于ConstraintValidator<StringConstraint, List ???>
我的请求对象是:

@JsonProperty(value = "items", required = true)
    @Schema(description = "List of items.", required = true)
    private List<String> items= new ArrayList<>();

所以我想对列表中的所有字符串应用我的验证器。我如何对我的列表应用@StringConstraint

0yg35tkg

0yg35tkg1#

是的,您可以在@Constraint注解的validatedBy属性中,使用验证器类别的逗号分隔清单,为一个条件约束加入更多的验证器。例如,您可以撰写:

@Constraint(validatedBy = {StringConstraintValidator.class, BlablaValidot.class})
public @interface MyConstraint {
  // other attributes
}

说明

@Constraint注解用于定义自定义约束注解,该注解可以应用于字段、方法、类等。validatedBy属性指定一个或多个类,这些类实现ConstraintValidator接口并提供验证带注解元素的逻辑。如果要检查值的不同方面或条件,可以对同一个约束使用多个验证器。例如,您可以使用一个验证程序来检查字符串的长度,而使用另一个验证程序来检查字符串的格式。
示例
下面是一些具有多个验证器的自定义约束注解的示例:

  • 使用两个验证器验证电话号码的@PhoneNumber注解:一个用于国家/地区代码,一个用于数字格式。
@Constraint(validatedBy = {CountryCodeValidator.class, PhoneNumberValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface PhoneNumber {
  String message() default "Invalid phone number";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}
  • @Password注解,使用三个验证器验证密码:一个用于最小长度,一个用于最大长度,一个用于特殊字符的存在。
@Constraint(validatedBy = {MinLengthValidator.class, MaxLengthValidator.class, SpecialCharValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
  String message() default "Invalid password";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
  int minLength() default 8;
  int maxLength() default 20;
  String specialChars() default "!@#$%^&*";
}
xpcnnkqh

xpcnnkqh2#

Java中的自定义验证器是一种定义您自己的规则以验证对象或参数数据的方法。您可以使用javax.validation API来创建和使用自定义验证器。该API由两个主要组件组成:注解和验证器。
注解用于声明要应用于数据的约束。注解通常位于要验证的字段或参数上。您可以使用API提供的内置注解,如@NotNull、@Size、@Pattern等,也可以为自定义约束定义自己的注解。
验证器是实现ConstraintValidator接口并提供根据约束验证数据的逻辑的类。该接口有两个通用参数:A(注解类型)和T(数据类型)。该接口有两个方法:initialize和isValid. initialize方法用于初始化带有annotation属性的验证器,isValid方法用于根据annotation检查数据是否有效。
要为列表类型创建自定义验证器,需要将列表类型指定为ConstraintValidator接口的第二个泛型参数,并实现isValid方法来迭代列表元素并分别验证它们。您也可以在自定义验证器中使用其他注解或验证器来重用现有的验证规则。

示例

下面是一个示例,说明如何为字符串列表创建自定义验证器,以检查每个元素是否为有效数字。

定义注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = StringConstraintValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface NumberConstraint {
    String message() default "Invalid number";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

定义验证器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;

public class StringConstraintValidator implements ConstraintValidator<NumberConstraint, List<String>> {

    @Override
    public void initialize(NumberConstraint constraintAnnotation) {
        // You can use this method to initialize the validator with the annotation attributes
    }

    @Override
    public boolean isValid(List<String> value, ConstraintValidatorContext context) {
        // You can use this method to validate the list elements
        if (value == null || value.isEmpty()) {
            return true; // You can change this to false if you want to reject null or empty lists
        }
        for (String s : value) {
            try {
                Double.parseDouble(s); // Try to parse the string as a double
            } catch (NumberFormatException e) {
                return false; // If the string is not a valid number, return false
            }
        }
        return true; // If all the strings are valid numbers, return true
    }
}

应用注解

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

public class Request {

    @NotNull
    @NumberConstraint
    private List<String> numbers;

    // Constructor, getters, setters, etc.

}

public class Controller {

    public void processRequest(@Valid Request request) {
        // Do something with the request
    }

}
6rqinv9w

6rqinv9w3#

如果要对方法进行单元测试

public void processRequest(@Valid Request request) {
  // Do something with the request
}

则输出应该是异常或错误消息,这取决于您处理验证的方式。

说明

@Valid注解用于指示参数在进入方法之前应该被验证。这意味着请求对象应该有一些约束或规则来定义什么使它有效或无效。例如,请求可能有一个必填字段、最大长度、特定格式等。
如果要求对象不符合这些限制,则验证将会失败,且不会执行方法。相反地,会掷回例外状况或传回错误消息,视验证机制的实作而定。这是为了防止方法行程可能会造成未预期行为或错误的无效要求。

示例

假设Request类具有以下约束:

public class Request {

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

  @Email
  private String email;

  // getters and setters
}

这意味着请求对象应该有一个非空的名称,长度在1到10个字符之间,以及一个有效的电子邮件地址。如果我们使用javax.validation API来执行验证,那么我们可以编写一个单元测试,如下所示:

import static org.junit.Assert.*;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.junit.Before;
import org.junit.Test;

public class RequestProcessorTest {

  private RequestProcessor requestProcessor;
  private Validator validator;

  @Before
  public void setUp() {
    requestProcessor = new RequestProcessor();
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    validator = factory.getValidator();
  }

  @Test
  public void testProcessRequestWithInvalidRequest() {
    // create an invalid request object
    Request request = new Request();
    request.setName(""); // empty name
    request.setEmail("invalid.com"); // invalid email

    // validate the request object
    Set<ConstraintViolation<Request>> violations = validator.validate(request);

    // assert that there are violations
    assertFalse(violations.isEmpty());

    // try to process the request
    try {
      requestProcessor.processRequest(request);
      fail("Should throw ConstraintViolationException");
    } catch (ConstraintViolationException e) {
      // assert that the exception contains the violations
      assertEquals(violations, e.getConstraintViolations());
    }
  }
}

相关问题