基于spring的组动态POJOvalidation

考虑以下pojo以供参考:

public class User{ private String username; private String firstName; private String middleName; private String lastName; private String phone; //getters and setters } 

我的应用程序基本上是基于spring-boot的REST API,它暴露了两个端点,一个用于创建用户,另一个用于检索用户。

“用户”属于某些类别, group-agroup-b等,我从post请求的标题中获取。

我需要在运行时validation用户数据,validation可能会因用户组而异。

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

正则表达式也可能因组而异。

我需要能够配置spring,以某种方式动态validation我的pojo,一旦它们被创建,它们各自的validation集将根据它们的组触发。

也许我可以创建一个yml / xml配置,这将允许我启用它?

我宁愿不用@NotNull@Pattern注释我的private String phone

我的配置如下:

 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 validators; public List getValidators() { return validators; } public void setValidators(List validators) { this.validators = validators; } } 

application.yml

 not-null: validators: - group: group-a target: user.username - group: group-b target: user.phone 

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

PS

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

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

正如我所看到的,对您的问题最简单的解决方案不是使用Spring或POJO本身,而是使用设计模式。

您描述的问题很容易通过策略模式解决方案解决。

您将策略与您在请求中期望的标头匹配,描述用户的类型,然后在策略本身内执行所述validation。

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

这是来自维基书籍的链接以及该模式的详细说明

战略模式

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

 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(); } } 

您可以向User POJO添加策略属性,并在收到发布请求时为该属性分配正确的Strategy实施。

每当您需要validation该用户的数据时,您只需调用已分配策略的validate方法。

如果每个User都可以适应多种策略,则可以将List添加为属性而不是单个属性。

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

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

希望这可以帮助。

您可以使用validation组来控制哪个类型的用户被validation哪个字段。 例如:

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

然后,您使用您提到的请求中的标头来决定要对哪个组进行validation。

有关完整示例,请参阅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> 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内置的validation支持在注释中设置为每个组validation哪些字段的映射。

我能够使用Jayway JsonPath解决我的问题。 我的解决方案如下:

  1. 在API中添加一个filter,它具有缓存ServletRequestInputStream的能力,因为它只能读取一次。 要实现此目的,请点击此链接 。
  2. 创建一堆validation器,并在@ConfigurationProperties的帮助下在application.yml文件中配置它们。 要实现此目的,请点击此链接
  3. 创建一个包装器,其中包含所有validation器作为列表,并使用@ConfigurationProperties和以下配置对其进行初始化:

     validators: regexValidators: - target: $.userProfile.lastName pattern: '[A-Za-z]{0,12}' group: group-b minMaxValidators: - target: $.userProfile.age min: 18 max: 50 group: group-b 
  4. 使用标头中的调用此包装器中的validate方法,然后调用各个validation器的validation。 为此,我在包装器中编写了以下代码:

     public void validate(String input, String group) { regexValidators.stream() .filter(validator -> group.equals(validator.getGroup())) .forEach(validator -> validator.validate(input)); minMaxValidators.stream() .filter(validator -> group.equals(validator.getGroup())) .forEach(validator -> validator.validate(input)); } 

以及validation器中的以下方法:

 public void validate(String input) { String data = JsonPath.parse(input).read(target); if (data == null) { throw new ValidationException("Target: " + target + " is NULL"); } Matcher matcher = rule.matcher(data); if (!matcher.matches()) { throw new ValidationException("Target: " + target + " does not match the pattern: " + pattern); } } 

我创建了一个function正常的项目来演示validation,可以在这里找到。
我理解单独的答案可能不太清楚,请按照上面提到的url获取完整的源代码。