基于春季组

时间:2017-10-05 17:47:08

标签: java spring validation spring-boot jackson

考虑以下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-a group-b 等,这是我从帖子请求的标题中获得的。

我需要在运行时验证用户数据,验证可能会因用户组而有所不同。

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

正则表达式也可能因其群体而异。

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

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

我不想使用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;
    }
}


application.yml

not-null:
  validators:

    -
      group: group-a
      target: user.username

    -
      group: group-b
      target: user.phone

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

P.S。

请随时修改问题以使其更好。

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

3 个答案:

答案 0 :(得分:16)

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

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

您可以根据您在请求中期望的标题来匹配策略,该标题描述用户的类型,然后在策略本身内执行所述验证。

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

这是来自维基书籍的链接以及对模式的详细解释

Strategy Pattern

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

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实现。

每当您需要验证该用户的数据时,您只需调用指定策略的validate方法。

如果每个User适合多种策略,您可以添加List<Strategy>作为属性而不是单个属性。

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

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

希望这有帮助。

答案 1 :(得分:5)

您可以使用验证组来控制哪个类型的用户需要验证哪个字段。例如:

@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的内置验证支持在注释中设置为每个组验证哪些字段的映射,类似的结果。

答案 2 :(得分:1)

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

  1. 向您的API添加一个过滤器,该过滤器可以缓存InputStream的{​​{1}},因为它只能读取一次。为此,请按this link
  2. 创建一堆验证器,并在ServletRequest的帮助下在application.yml文件中配置它们。为此,请按照this link
  3. 进行操作
  4. 创建一个包装器,其中包含所有验证器作为列表,并使用@ConfigurationProperties和以下配置对其进行初始化:

    @ConfigurationProperties
  5. 使用标头中的调用此包装中的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 方法,然后调用各个验证器的validate。为此,我在包装器中编写了以下代码:

    validate
  6. 以及验证器中的以下方法:

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

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