SpringBoot 使用 Validation 数据校验,自定义校验

目录

@Validated跟@Valid的区别

实际生产应用

ConstraintViolationException和 MethodArgumentNotValidException异常



@Validated跟@Valid的区别

    在使用前先了解下@Validated跟@Valid的区别,我们点开注解就能看出两者的不同

// Target代表这个注解能使用在类/接口/枚举上,方法上以及方法的参数上
// 注意注意!!!! 它不能注解到字段上
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
// 在运行时期仍然生效(注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在)
@Retention(RetentionPolicy.RUNTIME)
// 这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
@Documented
public @interface Validated {
	// 校验时启动的分组
	Class[] value() default {};

}
// 可以作用于类,方法,字段,构造函数,参数,以及泛型类型上(例如:Main<@Valid T> )
// 简单来说,哪里都可以放
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface Valid {
    //没有提供任何属性
}

1、来源不同 @Valid 是javax.validation包下,而@Validated是spring自生的注解

2、作用范围不同 @Validated无法作用在字段上面

3、注解中的属性不同,如果我们需要分组校验,就需要使用@Validated

实际生产应用

       接下来我们在使用中去了解 Validation ,我们先实现自定义的校验,项目中会有很多状态枚举,我们规定前端传入状态规定特定的值,去实现校验。

首先引入jar

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-validation
        

 先自定义个注解

package com.example.demotest.validator.annotate;

import com.example.demotest.validator.StatusValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;


@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy ={StatusValidator.class})
public @interface Status {

    String[] statusType() default {};

    String message() default "状态传递有误";

    Class[] groups() default {};

    Class[] payload() default {};

}

新建自定义校验器,也是注解中validatedBy 指定的

package com.example.demotest.validator;

import com.example.demotest.validator.annotate.Status;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.List;

public class StatusValidator implements ConstraintValidator {

    private List typeStatus ;

    @Override
    public void initialize(Status constraintAnnotation) {
        typeStatus = Arrays.asList(constraintAnnotation.statusType());
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
       if(value !=null){
           if(!typeStatus.contains(String.valueOf(value))){
               return false;
           }
       }
        return true;
    }
}

测试实体类新建

package com.example.demotest.vo;

import com.example.demotest.validator.annotate.Status;
import lombok.Data;

import javax.validation.constraints.NotBlank;

@Data
public class IndexVO {


    @Status(statusType = {"1","2"})
    private Integer loginStatus;

    @NotBlank(message = "id不能为空")
    private String id;

    @NotBlank(message = "名称不能为空")
    private String name;

}

测试接口新建

package com.example.demotest.controller;

import com.example.demotest.vo.IndexVO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("test")
public class TestController {

    @RequestMapping("index")
    public String test(@Valid @RequestBody IndexVO indexVO){
       return indexVO.getName()+" login success";
    }


}

这里我们统一捕获下异常,这里需要捕获两种校验异常,具体什么时候会出现下面的异常,后面再说

package com.example.demotest.config;

import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

@RestControllerAdvice
public class ExceptionHandle {



    @ExceptionHandler(value = {ConstraintViolationException.class, MethodArgumentNotValidException.class})
    public String handleValidator(Exception e){
        String msg = null;
        if (e instanceof MethodArgumentNotValidException) {
            BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            // getFieldError获取的是第一个不合法的参数(P.S.如果有多个参数不合法的话)
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                msg = fieldError.getDefaultMessage();
            }
        } else if (e instanceof ConstraintViolationException) {
            /*
             * ConstraintViolationException的e.getMessage()形如
             *     {方法名}.{参数名}: {message}
             *  这里只需要取后面的message即可
             */
            msg = e.getMessage();
            if (msg != null) {
                int lastIndex = msg.lastIndexOf(':');
                if (lastIndex >= 0) {
                    msg = msg.substring(lastIndex + 1).trim();
                }
            }
            /// ValidationException 的其它子类异常
        } else {
            msg = "处理参数时异常";
        }
        return msg;
    }
}

执行结果:SpringBoot 使用 Validation 数据校验,自定义校验_第1张图片

ConstraintViolationException和 MethodArgumentNotValidException异常

不仅可以校验controller这种,也可以校验普通的方法,上面说的 ConstraintViolationException异常就是通过普通方法校验抛出的异常,普通方法校验,只有类上添加了@Vlidated注解,并且待校验的JavaBean上添加了@Valid的情况下校验才会生效。

SpringBoot 使用 Validation 数据校验,自定义校验_第2张图片

 如果我们在校验逻辑中使用到了springBean对象,我们可以在校验器中使用注解生成bean

例如上面的检验器,就可以和数据库或者中间件打交道了

package com.example.demotest.validator;

import com.example.demotest.validator.annotate.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.List;

@Component
public class StatusValidator implements ConstraintValidator {

    @Autowired
    private UserService userService;

    private List typeStatus ;

    @Override
    public void initialize(Status constraintAnnotation) {
        typeStatus = Arrays.asList(constraintAnnotation.statusType());
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
       if(value !=null){
           if(!typeStatus.contains(String.valueOf(value))){
               return false;
           }
       }
        return true;
    }
}

你可能感兴趣的:(spring,boot,java,spring)