背景:虽然Validator已经提供很多检验注解了,但是有些况还是不支持的,就比如要校验省市区三者的关系,这时就可以自定义注解来校验了
联合校验:多个字段一起校验,例如:省市区关系校验、国家店铺账号等,一些有相关联系的字段校验
Bean Validation是Java定义的一套基于注解的数据校验规范,主要功能是对传输的参数进行数据校验,通过配置Validation提供的注解可以很容易的完成对数据的校验
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
@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
注解作用:省市区关系校验
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 {};
}
实现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;
}
}
这样自定义联合校验注解功能就好了
场景:接口添加地址,校验省市区的关系
@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;
}
这样异常就会在这里捕获,优雅的提示给前端
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(","));
}
}
大公告成 END!