别再乱用了,这才是 @Validated 和 @Valid 的真正区别和用法!

12,135 阅读4分钟

前言

在平时写接口的时候,需要进行参数的校验,如果参数少的话,使用if else还可以,但是参数多的时候,要写一大堆if else校验,敲的太累也不优雅。

所以今天将介绍使用注解来进行参数校验,既方便,还优雅。

关注公众号:臻大虾,分享更多java干货

@valid和@Validated区别

@Validation@Valid进行了二次封装

区别@valid@validate
提供者spring-boot-starter-web里面,springboot 项目自带Spring 做得一个自定义注解,增强了分组功能
是否支持分组不支持支持,参数校验时,根据不同的分组采取不同的校验
使用位置构造函数、方法、方法参数、成员属性类、方法、方法参数,不能用于成员属性
嵌套校验支持,因为可以在成员属性上使用不支持

常用注解

  • 除了@Null,@ NotNull,@ NotBlank,@NotEmpty这四个外,其他所有的注解,传 null 时都会被当作有效处理
  • 注解常用参数值:message(校验不通过反馈的信息)
注解验证的数据类型备注
Null任意类型参数值必须是 Null
NotNull任意类型参数值必须不是 Null
NotBlank只能作用于字符串字符串不能为 null,而且字符串长度必须大于0,至少包含一个非空字符串
NotEmptyCharSequence Collection Map Array参数值不能为null,且不能为空 (字符串长度必须大于0,空字符串(“ ”)可以通过校验)
Size(min,max )CharSequence Collection Map Array字符串:字符串长度必须在指定的范围内 Collection:集合大小必须在指定的范围内 Map:map的大小必须在指定的范围内 Array:数组长度必须在指定的范围内
Pattern(regexp)字符串类型验证字符串是否符合正则表达式
Min(value)整型类型参数值必须大于等于 最小值
Max(value)整型类型参数值必须小于等于 最大值
DecimalMin(value)整型类型参数值必须大于等于 最小值
DecimalMax(value)整型类型参数值必须小于等于 最大值
Positive数字类型参数值为正数
PositiveOrZero数字类型参数值为正数或0
Negative数字类型参数值为负数
NegativeOrZero数字类型参数值为负数或0
Digits(integer,fraction)数字类型参数值为数字,且最大长度不超过integer位,整数部分最高位不超过fraction位
AssertTrue布尔类型参数值必须为 true
AssertFalse布尔类型参数值必须为 false
Past时间类型(Date)参数值为时间,且必须小于 当前时间
PastOrPresent时间类型(Date)参数值为时间,且必须小于或等于 当前时间
Future时间类型(Date)参数值为时间,且必须大于 当前时间
FutureOrPresent时间类型(Date)参数值为时间,且必须大于或等于 当前日期
Email字符串类型被注释的元素必须是电子邮箱地址

校验场景

post请求校验

对象属性校验

  • 在入参对象的字段上添加校验注解,比如@Min
  • 在请求对象前面添加注解@Valid
@Data
public class User {
    @Min(value = 10,message = "年龄必须大于10岁")
    private Integer age;
}
​
@PostMapping("checkBodyParam")
public String checkBodyParam(@RequestBody @Valid User user){
  return "ok";
}

当age=2时,校验不通过,提示年龄必须大于10岁

image-20230727115321573

嵌套属性校验

  • 在嵌套对象上添加注解valid
  • 在请求对象前面添加注解valid
@Data
public class UserClass {
    private String className;
    @Valid
    private User user;
}
​
@PostMapping("checkBodyMultilevelParam")
public String checkBodyMultilevelParam(@RequestBody @Valid UserClass userClass){
  return "ok";
}

当age=2时,校验不通过,提示年龄必须大于10岁

image-20230727120123441

集合参数校验

  • 类上添加@Validated
  • 在请求对象前面添加注解@valid,用@validate没有效果
@RestController
@RequestMapping("/paramTest")
@Validated
public class ParamTestController {
  @PostMapping("checkList")
  public String checkList(@RequestBody @Valid List<User> users) {
    return "ok";
  }
}

但是如果要分组校验呢,只能用validate,但是validate又没有效果,怎么办呢。

方法一:

新建对象,将list当做属性

缺点:这样修改的话,请求的参数结构就会改变.

方法二:

  • 实现list
  • 在list属性上添加valid注解

这样ValidList与java.util.List的对外功能完全一致,无需改变集合结构

@Data
public class ValidList<E> implements List<E> {
​
    @Valid
    private List<E> list = new LinkedList<>();
    @Override
    public int size() {
        return list.size();
    }
    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }
    @Override
    public boolean contains(Object o) {
        return list.contains(o);
    }
    @Override
    public Iterator<E> iterator() {
        return list.iterator();
    }
    @Override
    public Object[] toArray() {
        return list.toArray();
    }
    @Override
    public <T> T[] toArray(T[] a) {
        return list.toArray(a);
    }
    @Override
    public boolean add(E e) {
        return list.add(e);
    }
    @Override
    public boolean remove(Object o) {
        return list.remove(o);
    }
    @Override
    public boolean containsAll(Collection<?> c) {
        return list.containsAll(c);
    }
    @Override
    public boolean addAll(Collection<? extends E> c) {
        return list.addAll(c);
    }
    @Override
    public boolean addAll(int index, Collection<? extends E> c) {
        return list.addAll(index, c);
    }
    @Override
    public boolean removeAll(Collection<?> c) {
        return list.removeAll(c);
    }
    @Override
    public boolean retainAll(Collection<?> c) {
        return list.retainAll(c);
    }
    @Override
    public void clear() {
        list.clear();
    }
    @Override
    public E get(int index) {
        return list.get(index);
    }
    @Override
    public E set(int index, E element) {
        return list.set(index, element);
    }
    @Override
    public void add(int index, E element) {
        list.add(index, element);
    }
    @Override
    public E remove(int index) {
        return list.remove(index);
    }
    @Override
    public int indexOf(Object o) {
        return list.indexOf(o);
    }
    @Override
    public int lastIndexOf(Object o) {
        return list.lastIndexOf(o);
    }
    @Override
    public ListIterator<E> listIterator() {
        return list.listIterator();
    }
    @Override
    public ListIterator<E> listIterator(int index) {
        return list.listIterator(index);
    }
    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        return list.subList(fromIndex, toIndex);
    }
}
​
  @PostMapping("checkValidList")
  public String checkValidList(@RequestBody @Valid ValidList<User> users) {
    return "ok";
  }

image-20230727161714930

get请求参数校验

  • 在类上使用@Validated注解
  • 在参数前面添加参数校验的注解
@RestController
@RequestMapping("/paramTest")
@Validated
public class ParamTestController {
  @GetMapping("checkParam")
  public String checkParam(@RequestParam  @Max(value = 99, message = "不能大于99岁") Integer age) {
    return "ok";
  }
  
  @GetMapping("checkPath/{id}")
  public String checkPath(@PathVariable  @Pattern(regexp = "^[0-9]*$", message = "id参数值必须是正整数") String id)   {
    return "ok";
  }
}

image-20230727140607340