基于春季组的动态 POJO 验证

2022-09-04 07:56:15

请考虑以下 pojo 作为参考:

public class User{

    private  String username;
    private String firstName;
    private String middleName;
    private String lastName;
    private String phone;

    //getters and setters

}

我的应用程序基本上是一个基于弹簧引导的REST API,它公开了两个端点,一个用于创建用户,另一个用于检索用户。

“用户”属于某些类别,组-a组-b等,我从帖子请求的标题中获得。

我需要在运行时验证用户数据,并且验证可能因用户组而异。

例如,属于组 a 的用户可能将电话号码作为可选字段,而它可能是其他组的必填字段。

正则表达式也可能根据其组而有所不同。

我需要能够配置spring,以便在创建它们后立即以某种方式动态验证我的pojo,并且它们各自的验证集会根据它们的组触发。

也许我可以创建一个yml / xml配置,允许我启用此功能?

我宁愿不注释我的 with 和 .private String phone@NotNull@Pattern

我的配置如下:

public class NotNullValidator implements Validator {
    private String group;
    private Object target;

    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public void validate(Object o) {
        if (Objects.nonNull(o)) {
            throw new RuntimeException("Target is null");
        }
    }
}


public interface Validator {
    void validate(Object o);
}


@ConfigurationProperties(prefix = "not-null")
@Component
public class NotNullValidators {
    List<NotNullValidator> validators;

    public List<NotNullValidator> getValidators() {
        return validators;
    }

    public void setValidators(List<NotNullValidator> validators) {
        this.validators = validators;
    }
}


应用程序.yml

not-null:
  validators:

    -
      group: group-a
      target: user.username

    -
      group: group-b
      target: user.phone

我想将我的应用程序配置为以某种方式允许验证程序选择它们的目标(实际对象,而不是yml中提到的字符串),并在它们的目标上调用它们各自的目标。public void validate(Object o)

附言

请随时编辑问题以使其更好。

我正在使用jackson进行序列化和反序列化JSON。


答案 1

在我看来,解决您的问题的最简单方法不是使用Spring或POJO本身,而是使用设计模式。

您描述的问题可以通过策略模式解决方案轻松解决。

匹配请求中预期的标头要使用的策略,该标头描述用户类型,然后在策略本身内执行所述验证。

这将允许您在整个方法中使用相同的POJO,并根据每种类型的用户策略处理/解析和验证数据的细节。

这是来自维基书籍的链接,其中包含对模式的详细说明

策略模式

假设您有一个策略的基本界面:

interface Strategy { 

    boolean validate(User user);
}

对于 2 种不同类型的用户,您有 2 种不同的实现:

public class StrategyA implements Strategy {

    public boolean validate(User user){

         return user.getUsername().isEmpty();
    }
}

public class StrategyB implements Strategy {

    public boolean validate(User user){

         return user.getPhone().isEmpty();
    }
}

将策略属性添加到 POJO,并在收到发布请求时将 的正确实现分配给该属性。UserStrategy

每次需要验证该用户的数据时,您只需调用分配策略的方法即可。validate

如果每个策略都可以容纳多个策略,则可以将 a 添加为属性,而不是单个属性。UserList<Strategy>

如果您不想更改POJO,则必须在每次收到发布请求时检查哪个是正确的策略。

除了该方法之外,您还可以添加特定于每个策略的方法来处理数据。validate

希望这有帮助。


答案 2

可以使用验证组来控制对哪个字段进行验证的用户类型。例如:

@NotBlank(groups = {GroupB.class})
private String phone;

@NotBlank(groups = {GroupA.class, GroupB.class})
private String username;

然后,使用您提到的请求中的标头来决定要验证的组。

有关完整示例,请参阅 http://blog.codeleak.pl/2014/08/validation-groups-in-spring-mvc.html?m=1


进行了更新,以包括更全面的示例:

public class Val {
    private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

    public boolean isValid(User user, String userType) {
        usergroups userGroup = usergroups.valueOf(userType);
        Set<ConstraintViolation<User>> constraintViolations = validator.validate(user, userGroup.getValidationClass());
        return constraintViolations.isEmpty();
    }

    public interface GroupA {}
    public interface GroupB {}

    public enum usergroups {
        a(GroupA.class),
        b(GroupB.class);

        private final Class clazz;

        usergroups(Class clazz) {
            this.clazz = clazz;
        }

        public Class getValidationClass() {
            return clazz;
        }
    }
}

这不使用 application.yaml,而是在注释中设置每个组验证哪些字段的映射,使用Spring的内置验证支持的结果类似。


推荐