一、前言
最早在传统Spring MVC项目里,因为前端对接数据都是后端程序员自己对接的,入参很明确,后端的参数校验自然可有可无,主要看项目的安全要求和工期要求。因此后端参数校验虽然很早就用,但是断断续续,不甚了了。
这几年,项目基本都是前后端分离模式,后端必要的参数校验自然不可少。每次使用,总是要在网上查找相应注解;同时有些不常用用法或奇怪BUG在做完后没及时总结,再一次遇到才后悔莫及。
因此,开一篇专门的笔记,来记录常用的注解和用法以供工作中查阅,希望能坚持逐渐完善吧。
二、常用注解
注解 | 作用 |
---|---|
@Null | 验证对象是否为 null |
@NotNull | 验证对象是否不为 null, 无法查检长度为 0 的字符串 |
@NotBlank | 字符串不为null,并且字符串trim()以后length要大于0 |
@NotEmpty | 集合或字符串不为空 |
@AssertTrue | Boolean 对象是否为 true |
@AssertFalse | Boolean 对象是否为 false |
@Size(min=, max=) | 验证对象(Array, Collection , Map, String)长度是否在给定的范围之内 |
@Length(min=, max=) | 验证字符串长度介于 min 和 max 之间 |
@Past | 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期 |
@PastOrPresent | 过去或者现在 |
@Future | 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期 |
@FutureOrPresent | 将来或者现在 |
@Min | 验证 Number 对象是否大等于指定的值 |
@Max | 验证 Number 对象是否小等于指定的值 |
@DecimalMax | 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过 BigDecimal定义的最大值的字符串表示 .小数 存在精度 |
@DecimalMin | 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过 BigDecimal定义的最小值的字符串表示 .小数 存在精度 |
@Digits(integer=,fraction=) | 数字格式检查。integer指定整数部分的最大长度,fraction指定小数部分的最大长度 |
@Positive | 数字,正数 |
@PositiveOrZero | 数字,正数或0 |
@Negative | 数字,负数 |
@NegativeOrZero | 数字,负数或0 |
@Range(min=, max=) | 被指定的元素必须在合适的范围内 |
@Pattern(regex=) | 字符串必须匹配正则表达式 |
@Valid | 嵌套验证、级联验证,如对象属性 |
三、校验方式
- 1、在Controller方法参数前加@Valid注解
- a)此处,@Valid 可以换成 @Validated;这两个有一些细节差别,待后期使用中完善吧;
- b) @Valid 位置也可以放置在方法上或类上,产生的验证范围会发生变化,习惯放于参数前,更灵活。
- c) 在验证参数后面加 BindingResult bindingResult,bindingResult会自动接收错误信息。
- 2、 手动校验
- a) 如果我们的数据不是来自接口传值,没有通过Controller层,但是又希望验证 Bean的参数有效性,那么可以通过手动调用 ValidatorFactory 来进行校验;
- b) 示例如下:
// Bean 验证器 ValidatorFactory vf = Validation.buildDefaultValidatorFactory(); Validator validator = vf.getValidator(); // 校验的业务对象:demo,Bean 类型:Demo Set<ConstraintViolation<Demo>> checkSet = validator.validate(demo); if (CollectionUtils.isNotEmpty(checkSet)) { // Bean验证不通过 throw new ServiceException(checkSet.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","))); }
四、问题记录
- 1、 级联验证
- a) 在Bean中存在对象属性或集合属性,需要级联验证对象中的属性时,需在属性上加“@Valid”注解,之前犯过傻,记录一下;
- b) 示例如下:
public class DemoDto { @Size(min = 1, message = "集合为空") @Valid private List<Detail> list;
- 2、 List 集合参数验证
- a) 可能遇到如下情况,保存一组数据,所以入参是对象集合,示例如下:
- b) 这种情况,想要验证集合非空及对象的属性有效性,常规方式是无效的;
- c) 参照网上的解决方式,既然集合在对象里可以级联验证,那么我们自定义一个集合类,实现List接口,示例如下:
// 定义校验集合类 public class ValidatorList<E> implements List<E> { @NotEmpty(message = "数据为空") @Valid private List<E> list; public List<E> getList() { return list; } public void setList(List<E> list) { this.list = list; } public ValidatorList() { this.list = new ArrayList<E>(); } public ValidatorList(List<E> list) { this.list = list; } @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); } } // 使用新集合类接收参数 // 控制器 @RequestMapping(value = "/save", method = RequestMethod.POST) @ResponseBody public int save(@RequestBody @Valid ValidList<SaveDto> list, BindingResult bindingResult) { // 省略业务代码 }