java后端参数校验validaction(用法详解)

Posted 野生java研究僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java后端参数校验validaction(用法详解)相关的知识,希望对你有一定的参考价值。

1.前言

beanvalidation官网:https://beanvalidation.org/(是规范,api接口)

hibernate官网:https://hibernate.org/validator/(是beanvalidation的最佳实现)

添加依赖包

hibernate-validator 或jakarta.activation


    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.0.2.Final</version>
    </dependency>

    <!-- jakarta 规范版本,和javax版本内容一样,只是包名不同而已 -->
    <dependency>
      <groupId>jakarta.activation</groupId>
      <artifactId>jakarta.activation-api</artifactId>
      <version>2.0.1</version>
    </dependency>

springBoot项目可以引入这个:


     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-validation</artifactId>
         <version>1.4.0.RELEASE</version>
     </dependency><dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

校验提示信息


    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-el</artifactId>
      <version>9.0.29</version>
    </dependency>

2.初体验

ValidationUtil工具类

public class ValidationUtil {
    // 线程安全
    private static Validator validator ;
    static{
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    /**
     *  校验
     * @param userInfo 校验的对象
     * @return 校验失败的信息
     */
    public static List<String> valid(UserInfo userInfo){
        // 没有通过,则set里面就哟校验信息
        Set<ConstraintViolation<UserInfo>> set = validator.validate(userInfo);
        List<String> list = set.stream().map(v -> "属性:" + v.getPropertyPath()
                + " 属性值:" + v.getInvalidValue()
                + " 提示信息:" + v.getMessage()).collect(Collectors.toList());
        list.stream().forEach(System.out::println);
        return list;

    }
}

需要校验的Bean

public class UserInfo {

    private Long id;
    @Null
    private String name;

    private Integer age;

    private String email;

    private String phone;

    private LocalDateTime birthDay;

    private String personalPage;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public LocalDateTime getBirthDay() {
        return birthDay;
    }

    public void setBirthDay(LocalDateTime birthDay) {
        this.birthDay = birthDay;
    }

    public String getPersonalPage() {
        return personalPage;
    }

    public void setPersonalPage(String personalPage) {
        this.personalPage = personalPage;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", name='" + name + '\\'' +
                ", age=" + age +
                ", email='" + email + '\\'' +
                ", phone='" + phone + '\\'' +
                ", birthDay=" + birthDay +
                ", personalPage='" + personalPage + '\\'' +
                '}';
    }
}

校验

    @Test
    void test2(){
        UserInfo userInfo = new UserInfo();
        userInfo.setName(null);
        ValidationUtil.valid(userInfo);
    }

校验结果:

3.JSR校验注解

可以在javax.validation.constraints包下看到支持的所有注解

注解注解作用说明
@Null被注释的元素值必须为 null
@NotNull被注释的元素值必须不为 null
@Pattern(regex=)被注释的元素字符串必须符合指定的正则表达式
@Size(max=, min=)集合元素数量必须在min和max范围内
@AssertTrue被注释的元素必须为 true
@AssertFalse被注释的元素必须为 false
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值(为null校验通过)
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值(为null校验通过
@Range(min,max)数字必须在min和max范围内(为null校验通过)
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Email字符串必须是Email地址
@Safehtml字符串必须是安全的html
@URL字符串必须是合法的URL

| @Size(max,min) | 限制字符长度必须在min到max之间 |

4.Hibernate Validator校验注解:

可以在org.hibernate.validator.constraints包下看到支持的所有注解【该注解中有说明支持那些数据类型】,重复的我就省略掉了

注解注解作用说明
@NotBlank(message =)验证字符串非null,且trim后长度必须大于0
@Length(min=,max=)被注释的字符串的大小必须在指定的范围内
@NotEmpty被注释的字符串的必须非空(null,"")

| @Valid | 对bean实体类进行级联校验 |

注:每个注解的属性值中都有一个message属性,可以自己定义校验未通过消息

  @Min(value = 18,message = "年龄小于{value}岁,校验失败")
    private Integer age; // 大于等于18

// 属性:age 属性值:17 提示信息:年龄小于18岁,校验失败

5.分组校验

默认的组为:javax.validation.groups.Default

例如:我们一个字段Id,需要在不同的场景下,进行不同的校验,向下面这样的方式就行不可以的,需要进行一个分组

    @NotNull // 适应于修改
    @Null //适应与新增
    private Long id;

结局方案:使用分组校验 在实体类中定义需要分组的接口标记

public class UserInfo {
    // 新增组标记接口
    public interface UserInfoAdd{

    }
    // 修改组标记接口
    public interface UserInfoUpdate{

    }

    @NotNull(groups = {UserInfoUpdate.class}) // 适应于修改
    @Null(groups = {UserInfoAdd.class}) //适应与新增
    private Long id;
}

ValidationUtil中稍作修改,进行校验的时候,指定使用那个组进行校验,记得加上默认组,避免之前的校验规则失效

public class ValidationUtil {
    // 线程安全
    private static Validator validator ;
    static{
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    /**
     *  校验
     * @param userInfo 校验的对象
     * @return 校验失败的信息
     */
    public static  List<String> valid(UserInfo userInfo, Class<?> group){
        // 没有通过,则set里面就哟校验信息
        Set<ConstraintViolation<UserInfo>> set = validator.validate(userInfo, Default.class,group);
        List<String> list = set.stream().map(v -> "属性:" + v.getPropertyPath()
                + " 属性值:" + v.getInvalidValue()
                + " 提示信息:" + v.getMessage()
                + " 消息模板:"+v.getMessageTemplate()
        ).collect(Collectors.toList());
        list.stream().forEach(System.out::println);
        return list;

    }
}

6.级联校验

假设我们在UserInfo中有一个Grade属性然后设置@NotNull是可以校验成功的,但是我们Grade类本身字节有个number属性,也设置了@NotBlank,此时在进行校验的时候就不可以了,无法校验,此时就需要使用级联校验。只需要在引用的对象上加上 @Valid,被引用的对象中的校验规则也会随着生效

    @NotNull
    //在被引用对象上加上@Valid才可以完成级联校验
    @Valid
    private Grade grade;

7.自定义注解

假设我们现在业务中,有一个falg值来标识,表示我们的业务逻辑,比如说订单状态:1:未分配,2:已分配,3:处理完成,这样的话,显然原有的功能无法满足我们的需求,我们可以使用自定义注解来完成。

1.先自定义注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: compass
 * @Date: 2021-11-08-17:49
 * @Version:1.0
 * @Description: 自定义注解:被标注的字段属性值必须是:1,2,3其中一个
 */
@Target({  FIELD })
@Retention(RUNTIME)
@Documented
// 说明当前注解@UserInfoFlag要被谁来完成校验工作
@Constraint(validatedBy = { FlagValidation.class})
public @interface UserInfoFlag {

    String message() default "校验不通过,flag不符合要求";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };
}

2.对自定义注解的校验功能实现


/**
 * Created with IntelliJ IDEA.
 *
 * @Author: compass
 * @Date: 2021-11-08-17:47
 * @Version:1.0
 * @Description: 自定义注解
 */
//ConstraintValidator<自定义的注解标识,需要处理的数据类型>
public class FlagValidation implements ConstraintValidator<UserInfoFlag,String> {

    @Override
    public void initialize(UserInfoFlag constraintAnnotation) {

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 新建一个集合,放入1,2,3 如果如果集合中未包含value就校验失败
        Set<String> set = new HashSet<>();
        set.add("1");
        set.add("2");
        set.add("3");
        return set.contains(value);
    }
}

使用:在需要校验的字段上加上 @UserInfoFlag 注解即可

    @UserInfoFlag(message = "校验失败,flag必须是 1,2,3中的一个")
    private String flag;

8.快速失败校验

    /**
     *  只要有一个校验失败,立刻返回结果,生效的不予校验
     * @param userInfo 校验的对象
     * @return 校验失败的信息
     */
    public static <c> List<String> validFastFail(UserInfo userInfo, Class<?> group){

        // 没有通过,则set里面就哟校验信息                              [加上默认组,避免之前的校验规则失效]
        Set<ConstraintViolation<UserInfo>> set = failFast.validate(userInfo, Default.class,group);
        List<String> list = set.stream().map(v -> "属性:" + v.getPropertyPath()
                + " 属性值:" + v.getInvalidValue()
                + " 提示信息:" + v.getMessage()
                + " 消息模板:"+v.getMessageTemplate()
        ).collect(Collectors.toList());
        list.stream().forEach(System.out::println);
        return list;

    }

9.非Bean入参校验

修改一下校验方法,使用参数校验的模式


    /**
     * 非bean校验入参
     * @param object 校验的对象
     * @param method 校验的方法
     * @param parameterValues 校验参数值
     * @param groups 使用那个组进行校验
     * @param <T>
     * @return 校验结果
     */
    public static <T> List<String> validNotBean(T object,
                                                Method method,
                                                Object[] parameterValues,
                                                Class<?>... groups){
Set<ConstraintViolation<T>> set = executableValidator.validateParameters(object, method, parameterValues, groups);

        List<String> list = set.stream().map(v -> "属性:" + v.getPropertyPath()
                + " 属性值:" + v.getInvalidValue()
                + " 提示信息:" + v.getMessage()
        ).collect(Collectors.toList());
        list.stream().forEach(System.out::println);

        return list;
    }

测试方法

public class UserInfoService {
    public String getByName(@NotNull String name){
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        Method method =null;
        try {
             method = this.getClass().getDeclaredMethod(methodName, String.class);
        }catch (Exception e){

        }
        ValidationUtil.validNotBean(this,method,new Object[]{name});
        return "admin";
    }
}

10spring mvc vaildaction校验

1.使用@Valid校验:@Valid的缺点,就是不能进行分组校验

springMVC之中已经提供了一系列的自动校验,我们只需要在需要校验的地方加上对应的注解即可

@RestController
public class UserController {

    @GetMapping("/addUser")
    public  Map<String, String> addUser(@Valid UserInfo userInfo, BindingResult errorResult){
        Map<String, String> map = new HashMap<>();
        if (errorResult.hasErrorsJava Bean Validation 最佳实践

Java Bean Validation 最佳实践

后端参数校验器v1.0(调用一个方法校验所有参数并得到校验结果,且包括错误原因)

java后端校验

后端参数校验,可以这么玩

Spring-boot参数校验:基本用法