目录
引入
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
用法
两方面:controller加注解@Validated或@Valid【加哪个由内部注解的包决定】,配合DTO中的属性注解实现
//controller
public class controller{
//controller中,方法参数前面要加注解
public void edit(@Validated DTO dto){
}
}
//实体类
public class DTO{
@NotNull(message="id不能为null")
private Integer id;
}
常用的注解校验
@NotBlank(message = "XX不能为空")
@Email(message="邮箱填写不规范")
时间类型
①后端向前端返回的值规范
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
//规范时间格式,时区采用北京东八区,默认的标准时区少8个小时
②前端向后端传入的值规范【注:只能用于url拼接的参数,放在实体中并没作用】
请求URL : http://localhost:9999/put?date=2020-03-07 13:13:13
@PostMapping("put")
public User put(@DateTimeFormat(pattern = "yyyy-MM-dd HH") @RequestParam Date date){
System.out.println(date); // Sat Mar 07 13:00:00 CST 2020 // 可以看到分和秒都被忽略了。
}
嵌套实体
//实体类
public class DTO{
@Valid
@Size(min=1,message="至少要有一个")
private List<AreaDTO> areaDTOList;
}
//内部嵌套类
publica class AreaDTO{
@NotBlank(message="不能为空")
private String code;
}
注:内部实体中要加@Valid的注解,实体内部的注解才能生效
自定义验证注解
以上的验证注解可能不够用,所以有了自定义验证注解。下面我们以枚举为例,不过最好不要用枚举类入参,因为不太好校验准确性;可以使用枚举类中的某个属性作为参数入参,配合自定义注解和实现。
①最终定义
public class DTO{
//枚举参数
@EnumVaildator(clazz= myEnum.class, message="枚举值不存在",method="getCode")
private Integer areaEnum;
}
②创建自定义验证注解
自定义验证注解:定义方法
@Constraint注解配合自定义验证注解,是此注解的实现方法类。内部是去找指定枚举中的指定字段,遍历查询跟入参字段对比是否存在
实现类和验证注解的定义可分成两个文件实现,下图是一个文件实现的
package com.hyj.annotation;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @author huyujie
* 枚举值入参检验注解
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Constraint(validatedBy = EnumValidator.EnumValidatorClass.class)
public @interface EnumValidator {
String message() default "入参值不在正确枚举中";
Class<?>[] groups() default {}; //这个方法要加上,不然会抛异常
Class<? extends Payload>[] payload() default {}; //这个方法要加上,不然会抛异常
Class<?> clazz(); //验证枚举类型
String method(); //验证枚举类方法
class EnumValidatorClass implements ConstraintValidator<EnumValidator, Object> {
private final List<Object> values = new ArrayList<>();
@Override
public void initialize(EnumValidator enumValidator) {
Class<?> clz = enumValidator.clazz();
Object[] objects = clz.getEnumConstants();
try {
Method method = clz.getMethod(enumValidator.method());
for (Object obj : objects) {
values.add(method.invoke(obj));
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
return Objects.isNull(value) || values.contains(value);
}
}
}
正则校验
@NotBlank
@Pattern(regexp = "[a-zA-Z0-9]", message = "只允许输入数字和字母")
private String strValue;
校验字段顺序
①字段上存在多个注解时,是按注解从上至下的顺序进行校验的
②@GroupSequence使用在group接口上。如下update方法,先校验group属于id.class的字段
其次是strValue
public interface TestValidGroup {
@GroupSequence(value = {StrValue.class})
interface Insert {
}
@GroupSequence(value = {Id.class, StrValue.class})
interface Update {
}
interface Id {
}
interface StrValue {
}
}
此时controller是这样用,跟之前不一样
//正确用法
@NotBlank(message = "ID不能为空", groups = {TestValidGroup.Id.class})
private String id;
//错误用法
@NotBlank(message = "ID不能为空", groups = {TestValidGroup.Update.class})
private String id;
下面说点干货:
@Valid和@Validated区别
分组
@Validated有分组,可以根据不同的分组用不同的机制
换句话说,增、改、查,可以定义一套DTO通过控制注解来实现参数校验
//controller
public class controller{
public void save(@Validated({TestValidGroup.Insert.class}) DTO dto){}
public void edit(@Validated({TestValidGroup.Update.class}) DTO dto){}
}
//实体类
public class DTO{
@NotNull(message="id不能为空",groups={Update.class})
private Ingeter id;
}
//建个空的就行
public interface TestValidGroup {
interface Insert {
}
interface Update {
}
}
作用地方
@Validated作用于:类型、方法、方法参数
@Valid作用于:方法、构造函数、方法参数、成员属性字段
所以,嵌套类的类内部注解实现,必须只能在嵌套类上加注解@Valid,而不是另外一个
来源
@Validated是Spring Validator提供的校验机制
@Valid是 Hibernate validation提供,需要引入下面的pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
关于验证信息的返回,异常处理
/**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 参数校验全局异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handlerValidator(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
List<ObjectError> allErrors = result.getAllErrors();
return Result.myError(allErrors);
}
}
@ExceptionHandler:用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常;RestControllerAdvice = ControllerAdvice + ResponseBody