扩展 Hibernate Validator 约束的问题

Problems extending a Hibernate Validator constraint

我正在尝试通过创建一个名为 @NotBlankUri.

的自定义约束来扩展 @NotBlank constraint 的行为以应用于 URIs

这是我的约束注释:

@Target({ METHOD, FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NotBlankUriValidator.class)
public @interface NotBlankUri {
    String message() default "{project.model.NotBlankUri.message}";

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

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

这里是 ConstraintValidator:

public class NotBlankUriValidator implements ConstraintValidator<NotBlankUri, URI> {

    public void initialize(NotBlankUri annotation) {
    }

    public boolean isValid(URI uri, ConstraintValidatorContext context) {
        NotBlankValidator nbv = new NotBlankValidator();
        return nbv.isValid(uri.toString(), context);
    }
}

问题是 ConstraintValidator 上的 isValid() 方法正在为 URI 参数获取 null 值。我认为这不应该发生,因为 @NotBlank 本身 is annotated @NotNull。事实并非如此,我尝试将 @NotNull 作为元注释添加到我的 @NotBlankUri,但这也没有达到预期的效果。我怎样才能让我的注释约束表现得像 @NotBlank,它似乎叠加在 @NotNull 的行为之上?

根据文档,您不能对非字符串的数据类型使用 @NotBlank 注释。

public @interface NotBlank

Validate that the annotated string is not null or empty. The difference to NotEmpty is that trailing whitespaces are getting ignored.

所以如果你声明你的验证器来验证一个字符串,一切都会好起来的,你可以这样写你的注释:

@Target({ METHOD, FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NotBlankUriValidator.class)
@NotBlank    
public @interface NotBlankUri {
    String message() default "{project.model.NotBlankUri.message}";

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

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

如果您对使用 URI class 1 不满意,您需要像这样自己执行自定义验证逻辑:

注解:

@NotNull(message="URI must not be null")
@Target({ METHOD, FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NotBlankUriValidator.class)
public @interface NotBlankUri {
    String message() default "URI must not be blank";

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

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

验证者:

public class NotBlankUriValidator implements ConstraintValidator<NotBlankUri, URI> {
    public void initialize(NotBlankUri annotation) {
    }

    public boolean isValid(URI uri, ConstraintValidatorContext context) {
        boolean isValid = true;
        System.out.println("URI: " + uri);
        //Leave null checks to your @NotNull constraint.
        //This is only here to prevent a NullPointerException on the next check.
        if(uri == null){
            return true;
        }
        if(uri.toString().isEmpty()){
            isValid = false;
        }
        return isValid;
    }
}

我运行以上带测试线束:

public class UriContainer {

    public UriContainer(URI uri){
        this.uri = uri;
    }
    @NotBlankUri
    private URI uri;

    public URI getUri() {
        return uri;
    }
}

public static void main(String[] args) throws URISyntaxException{
    UriContainer filledContainer = new UriContainer(new URI("Stuff"));
    UriContainer emptyContainer = new UriContainer(new URI(""));
    UriContainer nullContainer = new UriContainer(null);
    
    Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    
    Set<ConstraintViolation<UriContainer>> filledViolations = validator
            .validate(filledContainer);
    
    Set<ConstraintViolation<UriContainer>> emptyViolations = validator
            .validate(emptyContainer);
    
    Set<ConstraintViolation<UriContainer>> nullViolations = validator
            .validate(nullContainer);
    
    System.out.println("Filled: ");
    filledViolations.stream().forEach(System.out::println);
    System.out.println("Empty: ");
    emptyViolations.stream().forEach(System.out::println);
    System.out.println("Null: ");
    nullViolations.stream().forEach(System.out::println);
    
}

输出以下违规行为:

URI: Stuff
URI:
URI: null
Filled:
Empty:
ConstraintViolationImpl{interpolatedMessage='URI must not be blank', propertyPath=uri, rootBeanClass=class sandbox.UriContainer, messageTemplate='URI must not be blank'}
Null:
ConstraintViolationImpl{interpolatedMessage='URI must not be null', propertyPath=uri, rootBeanClass=class sandbox.UriContainer, messageTemplate='URI must not be null'}

如您所见,这允许您根据 URI 为空还是空输出不同的错误消息。只需确保如果您使用的是 javax.validation 注释,则检查您操作的数据类型。

1:顺便说一句,它在构造对象时执行验证,如果传递给构造函数的字符串违反 RFC 2396[,则会抛出 URISyntaxException =19=]