zoukankan      html  css  js  c++  java
  • 使用Spring的Validator进行校验

    使用Spring的Validator进行校验

    单对象校验

    让我们考虑一个小的数据对象:

    import lombok.Data;
    
    @Data
    public class Person {
    
        private String name;
        private int age;
    }
    

    我们将通过实现以下两个方法来提供Person类的验证行为
    org.springframework.validation.Validator接口的方法:

    • support(Class)-此验证程序可以验证提供的Class的实例吗?
    • validate(Object,org.springframework.validation.Errors)-验证给定对象,并在验证错误的情况下,向给定Errors对象注册

    实施Validator非常简单,尤其是当您知道Spring Framework也提供的ValidationUtils帮助器类时。

    import org.springframework.validation.Errors;
    import org.springframework.validation.ValidationUtils;
    import org.springframework.validation.Validator;
    
    /**
     * @author Created by niugang on 2020/3/30/16:45
     */
    public class PersonValidator implements Validator {
    
        /**
         * This Validator validates *just* Person instances
         */
        @Override
        public boolean supports(Class<?> clazz) {
            return Person.class.equals(clazz);
        }
    
        @Override
        public void validate(Object obj, Errors e) {
            ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
            Person p = (Person) obj;
            if (p.getAge() < 0) {
                e.rejectValue("age", "negative value","年龄不能为负值");
            } else if (p.getAge() > 110) {
                e.rejectValue("age", "too old","年龄不能超过110");
            }
        }
    
    }
    

    如您所见,ValidationUtils类上的静态rejectIfEmpty(..)方法用于拒绝“名称”属性(如果该属性为null或空字符串)。 看看ValidationUtils javadocs,看看它除了提供前面显示的示例外还提供什么功能。

    controller校验

        @PostMapping(value = "persons")
        public void savePerson(@RequestBody Person person) {
            log.info("请求参数:{}", person);
    
    
    //        PersonValidator validator = new PersonValidator();
    //        if(validator.supports(Person.class)){
    //            BindException errors = new BindException(person, "person");
    //            validator.validate(person,errors);
    //            List<ObjectError> allErrors = errors.getAllErrors();
    //            log.info("size="+allErrors.size());
    //            for (int i=0;i<allErrors.size();i++) {
    //               log.info(allErrors.get(i).getCode());
    //            }
    //
    //        }
    
            DataBinder binder = new DataBinder(person);
            binder.setValidator(new PersonValidator());
           // validate the target object
            binder.validate();
          // get BindingResult that includes any validation errors
            BindingResult results = binder.getBindingResult();
            log.info("results:{}", results);
        }
    
    嵌套对象校验

    虽然可以实现单个Validator类来验证丰富对象中的每个嵌套对象,但最好封装每个嵌套类的验证逻辑
    对象在其自己的Validator实现中。 一个“丰富”对象的简单示例是一个由两个String属性(第一个和第二个名称)和一个复杂的Address对象组成的Customer。 地址对象可以独立于客户对象使用,因此已经实现了独特的AddressValidator。 如果您希望CustomerValidator重用AddressValidator类中包含的逻辑,而无需进行复制和粘贴,则可以在您的CustomerValidator中依赖注入或实例化一个AddressValidator,并按如下方式使用它:

    import lombok.Data;
    
    /**
     * @author Created by niugang on 2020/3/30/18:42
     */
    @Data
    public class Address {
    
        private String  location;
    }
    
    
    import lombok.Data;
    
    /**
     * @author Created by niugang on 2020/3/30/18:40
     */
    @Data
    public class Customer {
    
        private String firstName;
        private String surname;
    
        private Address address;
    }
    
    
    import org.springframework.validation.Errors;
    import org.springframework.validation.ValidationUtils;
    import org.springframework.validation.Validator;
    
    /**
     * @author Created by niugang on 2020/3/30/18:44
     */
    public class AddressValidator implements Validator {
    
        /**
         * This Validator validates *just* Address instances
         */
        @Override
        public boolean supports(Class<?> clazz) {
            return Address.class.equals(clazz);
        }
    
        @Override
        public void validate(Object obj, Errors e) {
            ValidationUtils.rejectIfEmpty(e, "location", "location.empty");
            Address p = (Address) obj;
            if (p != null && p.getLocation() != null && p.getLocation().length() > 5) {
                e.rejectValue("location", "value too length", "长度不能超过5");
            }
        }
    }
    
    import org.springframework.validation.Errors;
    import org.springframework.validation.ValidationUtils;
    import org.springframework.validation.Validator;
    
    /**
     * @author Created by niugang on 2020/3/30/18:47
     */
    public class CustomerValidator implements Validator {
        private final Validator addressValidator;
    
        public CustomerValidator(Validator addressValidator) {
    
            if (addressValidator == null) {
                throw new IllegalArgumentException("The supplied [Validator] is " +
                        "required and must not be null.");
            }
            if (!addressValidator.supports(Address.class)) {
                throw new IllegalArgumentException("The supplied [Validator] must " +
                        "support the validation of [Address] instances.");
            }
            this.addressValidator = addressValidator;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return Customer.class.isAssignableFrom(clazz);
        }
    
        @Override
        public void validate(Object target, Errors errors) {
            ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
            ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
            Customer customer = (Customer) target;
            try {
                errors.pushNestedPath("address");
                ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
            } finally {
                errors.popNestedPath();
            }
        }
    }
    
    

    将代码解析为错误消息

    我们已经讨论了数据绑定和验证。输出与验证错误相对应的消息是我们需要讨论的最后一件事。在上面显示的示例中,我们拒绝了姓名和年龄字段。如果我们要使用MessageSource输出错误消息,则将使用拒绝字段时给出的错误代码(在这种情况下为“名称”和“年龄”)进行输出。当您从Errors接口调用(直接或间接使用,例如ValidationUtils类)rejectValue或其他拒绝方法之一时,底层实现不仅会注册您传入的代码,还会注册许多其他错误代码。它注册的错误代码由所使用的MessageCodesResolver确定。默认情况下,使用DefaultMessageCodesResolver,例如,它不仅使用您提供的代码注册消息,而且还包括包含您传递给拒绝方法的字段名称的消息。因此,如果您使用rejectValue(“ age”,“ too.darn.old”)拒绝字段,除了too.darn.old代码外,Spring还将注册too.darn.old.age和too.darn.old .age.int(因此第一个将包含字段名称,第二个将包含字段类型);这样做是为了方便开发人员,以帮助他们定位错误消息等。

    Spring Validator

    Spring 3对其验证支持进行了一些增强。 首先,现在完全支持JSR-303 Bean验证API。 其次,以编程方式使用时,Spring的DataBinder现在可以验证对象以及绑定到它们。 第三,Spring MVC现在支持声明式验证@Controller输入。

    JSR-303 Bean验证API概述

    JSR-303标准化了Java平台的验证约束声明和元数据。 使用此API,您可以使用声明性验证约束和运行时注释域模型属性强制执行。 您可以利用许多内置约束。 您也可以定义自己的自定义约束。

    单对象

    为了说明这一点,请考虑一个具有两个属性的简单PersonForm模型:

    public class PersonForm {
    private String name;
    private int age;
    }
    

    JSR-303允许您针对以下属性定义声明性验证约束:

    public class PersonForm {
    @NotNull
    @Size(max=64)
    private String name;
    @Min(0)
    private int age;
    }
    
     @PostMapping(value = "personforms.do")
     public void saveCustomer(@RequestBody @Validated PersonForm personForm) {
         log.info("请求参数:{}", personForm);
     }
    
    

    联级校验

    Bean Validation API不仅允许验证单个类实例,而且还可以完成对象图(级联验证)。 为此,只需注释表示一个
    如级联验证所示,使用@Valid引用另一个对象。

    import lombok.Data;
    
    import javax.validation.Valid;
    import javax.validation.constraints.NotNull;
    
    /**
     * @author Created by niugang on 2020/3/30/19:42
     */
    @Data
    public class Car {
    
        @NotNull
        private String type;
    
        @NotNull
        @Valid
        private PersonInfo driver;
    }
    
    
    import lombok.Data;
    
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;
    
    /**
     * @author Created by niugang on 2020/3/30/19:42
     */
    @Data
    public class PersonInfo {
    
        @NotNull
        @Size(min = 5)
        private String name;
    }
    
    

    联级校验集合

    import lombok.Data;
    import org.hibernate.validator.constraints.NotBlank;
    
    import javax.validation.constraints.Min;
    
    /**
     * @author Created by niugang on 2020/4/2/11:32
     */
    @Data
    public class Dog {
    
    
        @NotBlank
        private  String  name;
    
        @NotBlank
        private  String type;
    
        @Min(0)
        private int age;
    }
    
    
    import javax.validation.Valid;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Created by niugang on 2020/4/2/11:31
     */
    @Data
    public class Animal {
    
    
        @Min(1)
        private int count;
    
    
        @Valid
        @NotNull
        @Size(min = 1)
        private List<Dog> dogs= new ArrayList<>();
    
    }
    

    组校验

    Driver中的类Driver扩展Person并添加属性age和hasDrivingLicense。 驾驶员必须年满18岁(@Min(18))并具有驾驶执照(@AssertTrue)。 这些属性上定义的两个约束都属于该组DriverChecks只是一个简单的标记界面。

    import lombok.Data;
    
    import javax.validation.constraints.NotNull;
    
    /**
     * @author Created by niugang on 2020/4/2/14:44
     */
    @Data
    public class Person {
        @NotNull
        private String name;
    }
    
    
    import lombok.Data;
    
    import javax.validation.constraints.AssertTrue;
    import javax.validation.constraints.Min;
    
    /**
     * @author Created by niugang on 2020/4/2/14:45
     */
    @Data
    public class Driver extends Person {
        @Min(
                value = 18,
                message = "You have to be 18 to drive a car",
                groups = DriverChecks.class
        )
        public int age;
        @AssertTrue(
                message = "You first have to pass the driving test",
                groups = DriverChecks.class
        )
        public boolean hasDrivingLicense;
    }
    
    
    /**
     * @author Created by niugang on 2020/4/2/14:45
     */
    public interface DriverChecks {
    }
    
    

    controller

    以下请求 只会对标记为DriverChecks的属性进行校验

      @PostMapping(value = "drivers.do")
        public void cars(@RequestBody @Validated(DriverChecks.class) Driver driver, BindingResult result) {
            log.info("请求参数:{}", driver);
            log.info("请求参数 result:{}", result);
        }
    

    配置Bean验证提供程序

    Spring提供了对Bean验证API的全面支持。 这包括对将JSR-303 / JSR-349 Bean验证提供程序引导为Spring Bean的便捷支持。 这允许在应用程序中需要验证的任何地方插入javax.validation.ValidatorFactory或javax.validation.Validator。

    使用LocalValidatorFactoryBean将默认的验证器配置为Spring Bean:

    <bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
    

    上面的基本配置将触发Bean验证使用其默认的引导程序机制进行初始化。 预期Hibernate Validator之类的JSR-303 / JSR-349提供程序将出现在类路径中,并将自动检测到该提供程序。

    注入验证器

    LocalValidatorFactoryBean同时实现javax.validation.ValidatorFactory和javax.validation.Validator以及Spring的
    org.springframework.validation.Validator。 您可以将对这两个接口之一的引用注入需要调用验证逻辑的Bean中。

    如果您希望使用Bean验证,请注入对javax.validation.Validator的引用API直接:

    import javax.validation.Validator;
    @Service
    public class MyService {
    @Autowired
    private Validator validator;
    

    如果您的bean需要Spring Validation API,请注入对org.springframework.validation.Validator的引用:

    @RestController
    @RequestMapping("/validator/")
    @Slf4j
    public class TestValidatorController{
        
        private final Validator validator;
    
        @Autowired
        public TestValidatorController(Validator validator) {
            this.validator = validator;
        }
        
          @PostMapping(value = "v2/drivers.do")
          public void carss(@RequestBody Driver driver) {
            BindException bindException = new BindException(driver, "driver");
            validator.validate(driver,bindException);
            log.info("请求参数:{}", driver);
            log.info("请求参数 result:{}", bindException.getBindingResult());
        }
    
    }
    
    @Data
    public class Driver {
    
        @NotNull
        private String name;
        @Min(
                value = 18,
                message = "You have to be 18 to drive a car"
        )
    
        public int age;
    
        @AssertTrue(
                message = "You first have to pass the driving test"
        )
    
        public boolean hasDrivingLicense;
    
    }
    

    配置自定义约束

    每个Bean验证约束均由两部分组成。 首先,@Constraint批注声明约束及其可配置属性。 二,实施实现约束行为的javax.validation.ConstraintValidator接口。 要将声明与实现相关联,每个@Constraint批注都引用一个对应的ValidationConstraint实现类。 在运行时,当在域模型中遇到约束注释时,ConstraintValidatorFactory实例化引用的实现。

    默认情况下,LocalValidatorFactoryBean配置一个使用Spring创建ConstraintValidator实例的SpringConstraintValidatorFactory。 这使您的自定义ConstraintValidators可以像其他任何Spring Bean一样受益于依赖项注入

    下面显示的是一个自定义@Constraint声明的示例,后面是一个使用Spring进行依赖项注入的关联的ConstraintValidator实现:

    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy=MyConstraintValidator.class)
    public @interface MyConstraint {
    }
    
    import javax.validation.ConstraintValidator;
    public class MyConstraintValidator implements ConstraintValidator {
    @Autowired;
    private Foo aDependency;
    ...
    }
    

    如您所见,ConstraintValidator实现可能像其他任何Spring bean一样具有@Autowired的依赖项。

    基于spring的方法校验

    即就是在方法上写注解,进行参数校验

    Bean Validation 1.1支持的方法验证功能以及Hibernate Validator 4.3的自定义扩展也可以通过以下方式集成到Spring上下文中:MethodValidationPostProcessor bean的定义:

    <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
    

    为了有资格进行Spring驱动的方法验证,所有目标类都需要使用Spring的@Validated注释进行注释,并可以选择声明要使用的验证组。 看看使用MethodValidationPostProcessor javadocs进行Hibernate Validator和Bean Validation 1.1提供程序的设置详细信息

    配置一个DataBinder

    从Spring 3开始,可以使用Validator配置DataBinder实例。 配置完成后,可以通过调用binder.validate()来调用Validator。 任何验证错误都会自动添加到BindingResult。
    以编程方式使用DataBinder时,可将其用于在绑定到目标对象后调用验证逻辑:

    Foo target = new Foo();
    DataBinder binder = new DataBinder(target);
    binder.setValidator(new FooValidator());
    // bind to the target object
    binder.bind(propertyValues);
    // validate the target object
    binder.validate();
    // get BindingResult that includes any validation errors
    BindingResult results = binder.getBindingResult();
    

    在这里插入图片描述

  • 相关阅读:
    滚动 冻结 div demo
    JavaScript去除字符串两边空格trim
    window.showModalDialog以及window.open用法简介
    转: 分享我创业4年失败的经历
    [转]JavaScript break跳出多重循环
    showModalDialog 传值及刷新
    防止文字撑开表格,强制表格大小
    带记忆功能的表单
    checkbox 全选
    asp教程:关于jquery跨域彻底的解决方法
  • 原文地址:https://www.cnblogs.com/niugang0920/p/12689224.html
Copyright © 2011-2022 走看看