自定义Validator注解,实现联合字段校验

背景:虽然Validator已经提供很多检验注解了,但是有些况还是不支持的,就比如要校验省市区三者的关系,这时就可以自定义注解来校验了

联合校验:多个字段一起校验,例如:省市区关系校验、国家店铺账号等,一些有相关联系的字段校验

1. Validator是什么

Bean Validation是Java定义的一套基于注解的数据校验规范,主要功能是对传输的参数进行数据校验,通过配置Validation提供的注解可以很容易的完成对数据的校验

1.1. 添加Validation依赖

<dependency>
  <groupId>org.springframework.bootgroupId>
  <artifactId>spring-boot-starter-validationartifactId>
dependency>

1.2. 下面是常用的校验注解

@Null 限制只能为null
@NotNull 限制必须不为null
@NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0
@NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位
数不能超过fraction
@Future 限制必须是一个将来的日期
@Past 验证注解的元素值(日期类型)比当前时间早
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
注意:
@NotNull 适用于任何类型被注解的元素必须不能与NULL
@NotEmpty 适用于String Map或者数组不能为Null且长度必须大于0
@NotBlank 只能用于String上面 不能为null,调用trim()后,长度必须大于0

2. 自定义联合校验注解

注解作用:省市区关系校验

2.1. 注解类AddressValidation

import javax.validation.Constraint;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = {AddressValidator.class})
// 该注解用于类、接口(包括注解类型)或enum声明
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AddressValidation {

     /**
     * 校验失败时的返回信息
     */
    String message() default "联合字段校验失败";

    /**
     * 分组校验
     */
    Class<?>[] groups() default {};

    /**
     * 负载
     */
    Class<? extends Payload>[] payload() default {};


}

2.2. 自定义校验类ConstraintValidator

实现ConstraintValidator类,在isValid中写校验逻辑

这里是直接写死省市区必须为浙江省、杭州市、西湖区才可以,不然就报错,实际情况要去数据库查询

AddressDTO:是新增地址的传参DTO,这个按要求自己修改

import com.mamba.mybatisplus.dto.AddressDTO;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class AddressValidator implements ConstraintValidator<AddressValidation, Object> {

    @Override
    public void initialize(AddressValidation customValid) {
        // 初始化方法,如果需要从注解中读取参数,可以在这里做
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {

        if (value == null) {
            // 或者返回false,根据你的业务需求
            return true;
        }

        // 假设我们要校验的是一个具有特定字段的类,这里需要进行类型判断和字段值提取
        // 这个校验地址的省市区是否合法
        if (value instanceof AddressDTO) {
            AddressDTO addressDTO = (AddressDTO) value;
            // 这里正常需要去数据库或其他文件的省市区关系表中查询,这里为了方便直接写死
            // 这里假设我们要校验的地址是浙江省杭州市西湖区
            boolean isValid = "浙江省".equals(addressDTO.getProvince()) && "杭州市".equals(addressDTO.getCity()) && "西湖区".equals(addressDTO.getArea());
            // 校验通过则返回true,否则返回false
            return isValid;
        }

        // 对于非MyCustomClass类型的对象,我们默认其通过校验(或者你也可以让它失败)
        return false;
    }
}

这样自定义联合校验注解功能就好了

3. 使用自定义的联合校验注解

场景:接口添加地址,校验省市区的关系

3.1. api接口

@PostMapping("/addAddress")
public void addAddress(@Validated AddressDTO addressDTO){
    System.out.println("地址添加成功");
}

AddressDTO类上添加自定义注解:@AddressValidation(message = “省市区关系错误”)

具体代码如下

import com.mamba.mybatisplus.validator.AddressValidation;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;

@Data
// 自定义的联合校验注解
@AddressValidation(message = "省市区关系错误")
public class AddressDTO {

    @NotBlank(message = "省不能为空")
    @ApiModelProperty(value = "省")
    private String province;

    @NotBlank(message = "市不能为空")
    @ApiModelProperty(value = "市")
    private String city;

    @NotBlank(message = "区不能为空")
    @ApiModelProperty(value = "区")
    private String area;

    @NotBlank(message = "详细地址不能为空")
    @ApiModelProperty(value = "详细地址")
    private String detail;

}

3.2. 建一个全局异常拦截器

这样异常就会在这里捕获,优雅的提示给前端

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 */
@ControllerAdvice
@Slf4j
public class GlobalControllerAdvice {

    /**
     * 处理请求中使用@Valid验证路径中请求实体校验失败后抛出的异常,异常类型:org.springframework.validation.BindException
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public String bindExceptionHandler1(BindException e) {
        log.error("参数校验失败", e);
        return e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
    }


    /**
     * 处理请求参数格式错误 异常类型:javax.validation.ConstraintViolationException
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public String constraintViolationExceptionHandler(ConstraintViolationException e) {
        log.error("参数校验失败", e);
        return e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));
    }


    /**
     * 处理请求参数格式错误 异常类型:MethodArgumentNotValidException异常。
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public String methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        log.error("参数校验失败", e);
        return e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
    }


}

4. 请求接口,报错提醒

自定义Validator注解,实现联合字段校验_第1张图片

大公告成 END!

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