具有多个字段的Spring自定义批注验证 [英] Spring Custom Annotation Validation with multiple field

查看:162
本文介绍了具有多个字段的Spring自定义批注验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里有一个贪婪的小问题,希望这个问题也可以帮助其他想更多地了解注释验证的人

A little greedy question here, hope this one could also help others who want to know more about annotation validation

我目前正在学习Spring,现在,我计划尝试进行自定义的带注释的验证.

I am currently studying Spring, and for now, I am planning to try out the customize annotated validation.

我进行了很多搜索,现在我知道主要有两种验证方法,一种用于控制器,另一种是使用@Valid的注释方法

I have searched a lot and now I know there are mainly two kinds of validations, one is used for the controller, and the other is the annotation method using @Valid

这是我的情况: 假设我有两个或多个字段,当它们均为ALL NULL时可以为null. 但是,只有当这些字段之一包含除空字符串以外的任何值时,才要求这些字段具有输入.而且我有两个想法,但是不知道如何正确地实现它们.

So here's my scenario: Suppose I have two or more fields which can be null when they are ALL NULL. But only when one of those fields contains any value except an empty string, those fields are required to have input. And I had two ideas but didn't know how to implement them correctly.

这是课程示例:

public class Subscriber {
    private String name;
    private String email;
    private Integer age;
    private String phone;
    private Gender gender;
    private Date birthday;
    private Date confirmBirthday;
    private String birthdayMessage;
    private Boolean receiveNewsletter;

    //Getter and Setter
}

假设我希望Birthday和confirmBirthday字段都必须为null或相反,我可能想对它们每个使用一个注释来对其进行注释,如下所示:

Suppose I want that the birthday and confirmBirthday field need to be both null or the oppose, I may want to annotate them using one annotation for each of them and looks like this:

public class Subscriber {
    private String name;
    private String email;
    private Integer age;
    private String phone;
    private Gender gender;

    @NotNullIf(fieldName="confirmBirthday")
    private Date birthday;

    @NotNullIf(fieldName="birthday")
    private Date confirmBirthday;

    private String birthdayMessage;
    private Boolean receiveNewsletter;

    //Getter and Setter
}

所以我确实需要像这样创建验证注释:

So i do need to create the validation Annotation like this:

@Documented
@Constraint(validatedBy = NotNullIfConstraintValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface NotNullIf {

    String fieldName();

    String message() default "{NotNullIf.message}";
    Class<?>[] group() default {};
    Class<? extends Payload>[] payload() default {};
}

然后,我需要创建验证器本身:

And After that i will need to create the Validator itself:

public class NotNullIfConstraintValidator implements ConstraintValidator<NotNullIf, String>{

    private String fieldName;

    public void initialize(NotNullIf constraintAnnotation) {
        fieldName = constraintAnnotation.fieldName();
    }

    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(value == null) {
            return true;
        };
        //TODO Validation
        return false;
    }

}

那怎么可以实现呢?

对于另一个使用同一类作为示例的想法,该示例说我想要生日,confirmBirthday和BirthdayMessdage只能为null或同时是反对的. 这次我可能需要使用带注释的类进行跨字段验证.

So how can it be achievable?

For another idea using the same Class as an example which said that i want birthday, confirmBirthday and birthdayMessdage can only be null or the oppose at the same time. I may require to use the class annotated validation this time for cross-field validation.

这是我想对课程进行注释的方式:

Here's how i suppose to annotate the class:

@NotNullIf(fieldName={"birthday", "confirmBirthday", "birthdayMessage"})
public class Subscriber {
    //Those field same as the above one
}

因此,当该字段之一不为null时,还需要根据客户端大小输入其余字段. 有可能吗?

So when one of that field is not null, the rest of them also needs to be entered on the client size. Is it Possible?

我已经阅读了这篇文章:如何访问注释属性中描述的字段

I have read this article: How to access a field which is described in annotation property

但是我仍然对上面列出的那些元素的注释验证的工作方式感到困惑. 也许我需要对该代码进行一些详细的说明,甚至更糟的是,我可能需要进行一些基本的概念检查.

But I still confusing on how the annotation validation works from those elements I listed above. Maybe I need some detail explanation on that code or even worse I may need some basic concept inspection.

请帮助!

推荐答案

为此,您可以使用

For this you can use a type level annotation only because a field level annotation has no access to other fields!

我做了一些类似的事情以允许选择验证(确切地说,许多属性之一必须不为null).在您的情况下,@AllOrNone批注(或您喜欢的任何名称)将需要一个字段名称数组,并且您将带批注类型的整个对象提供给验证器:

I did something similar to allow a choice validation (exactly one of a number of properties has to be not null). In your case the @AllOrNone annotation (or whatever name you prefer) would need an array of field names and you will get the whole object of the annotated type to the validator:

@Target(ElementType.TYPE)
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = AllOrNoneValidator.class)
public @interface AllOrNone {
    String[] value();

    String message() default "{AllOrNone.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class AllOrNoneValidator implements ConstraintValidator<AllOrNone, Object> {
    private static final SpelExpressionParser PARSER = new SpelExpressionParser();
    private String[] fields;

    @Override
    public void initialize(AllOrNone constraintAnnotation) {
        fields = constraintAnnotation.value();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        long notNull = Stream.of(fields)
                .map(field -> PARSER.parseExpression(field).getValue(value))
                .filter(Objects::nonNull)
                .count();
        return notNull == 0 || notNull == fields.length;
    }
}

(正如您所说的那样,我使用SpEL允许甚至嵌套字段访问)

(As you said you use Spring I used SpEL to allow even nested fields access)

现在您可以注释您的Subscriber类型:

Now you can annotate your Subscriber type:

@AllOrNone({"birthday", "confirmBirthday"})
public class Subscriber {
    private String name;
    private String email;
    private Integer age;
    private String phone;
    private Gender gender;
    private Date birthday;
    private Date confirmBirthday;
    private String birthdayMessage;
    private Boolean receiveNewsletter;
}

这篇关于具有多个字段的Spring自定义批注验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆