目录
(5)@Digits(integer=,fraction=)
一.基础校验
1.数字校验
(1)@DecimalMax
- 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
- 不支持double、float类型(否则小数点会有精度丢失的可能)
- 限制必须为数字,最大值不能超过指定值
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @DecimalMax(value = "99") private String parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
输入值必须是数字并且小于等于99
- 发送请求校验入参-99999(成功)
- 发送请求校验入参0(成功)
- 发送请求校验入参1(成功)
- 发送请求校验入参99(成功)
- 发送请求校验入参100(失败)
(2)@DecimalMin
- 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
- 不支持double、float类型(否则小数点会有精度丢失的可能)
- 限制必须为数字,最小值不能低于指定值
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @DecimalMin(value = "99") private String parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
输入值必须是数字并且大于等于99
- 发送请求校验入参99999(成功)
- 发送请求校验入参100(成功)
- 发送请求校验入参99(成功)
- 发送请求校验入参89(失败)
- 发送请求校验入参0(失败)
- 发送请求校验入参-1(失败)
(3)@Max
- 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
- 不支持double、float类型(否则小数点会有精度丢失的可能)
- 限制必须为数字,最大值不能超过指定值
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Max(value = 10) private String parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
最大值不能超过10
- 发送请求校验入参10(成功)
- 发送请求校验入参9.99999999999999999999999999(成功)
- 发送请求校验入参0(成功)
- 发送请求校验入参-999999999999999999999(成功)
- 发送请求校验入参10.00000000000000000000000000001(失败)
(4)@Min
- 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
- 不支持double、float类型(否则小数点会有精度丢失的可能)
- 限制必须为数字,最小值不能低于指定值
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Min(value = 10) private String parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
最小值不能小于10
- 发送请求校验入参10(成功)
- 发送请求校验入参10.00000000000000000000001(成功)
- 发送请求校验入参0(失败)
- 发送请求校验入参9.999999999999999999999(失败)
- 发送请求校验入参-1(失败)
(5)@Digits(integer=,fraction=)
- 标注的元素可定义为BigDecimal、BigInteger、String、byte、short、int、long以及他们的包装类型
- 限制必须为数字类型,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Digits(integer = 2,fraction = 2) private String parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
输入值必须是数字并且是2位整数和2位小数
- 发送请求校验入参99.99(成功)
- 发送请求校验入参-99.99(成功)
- 发送请求校验入参0(成功)
- 发送请求校验入参999(失败)
- 发送请求校验入参-999(失败)
- 发送请求校验入参99.999(失败)
- 发送请求校验入参-99.999(失败)
- 发送请求校验入参999.999(失败)
2.字符串校验
(1)@NotBlank
- 入参不能为空,只支持String类型的校验
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @NotBlank private String parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
入参不能为空
- 发送请求校验入参空值(失败)
- 发送请求校验入参其他值(成功)
3.时间校验
(1)@Future
- 必须为未来时间
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Future private Date parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
- 发送请求校验入参2050-01-01T12:12:12(成功)
- 发送请求校验入参2000-01-01T12:12:12(失败)
(2)@Past
- 必须为过去时间
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Past private Date parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
- 发送请求校验入参2000-01-01T12:12:12(成功)
- 发送请求校验入参2050-01-01T12:12:12(失败)
(3)@DateTimeFormat
前端传至后端规定的格式
-
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
(4)@JsonFormat
后端转换date类型格式传至前端
-
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
4.集合校验
(1)@NotEmpty
- 入参不能为空,只支持List、Set、Map集合的校验
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @NotEmpty private List<String> parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@RequestBody @Valid DailyDTO dailyDTO){ return "ok"; } }
入参不能为空
- 发送请求校验入参空值(失败)
- 发送请求校验入参其他值(成功)
5.布尔值校验
- 注意:包装类型的Boolean没有默认值,可以检验空
- 注意:基础类型的boolean有默认值,空即为false,无法检验空值!!!
- 所以阿里的Java开发规范也是建议我们使用包装类型定义
(1)@AssertFalse
- 标注的元素必须定义为布尔类型
- 校验必须是null值或者布尔类型的false值
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @AssertFalse(message = "一定要为false") private Boolean parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
传递的参数如果不确定就传空,否则必须传false
- 发送请求校验入参false(成功)
- 发送请求校验入参null(成功)
- 发送请求校验入参true(失败)
(2)@AssertTrue
- 标注的元素必须定义为布尔类型
- 校验必须是null值或者布尔类型的true值
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @AssertTrue(message = "一定要为true") private Boolean parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
传递的参数如果不确定就传空,否则必须传true
- 发送请求校验入参true(成功)
- 发送请求校验入参null(成功)
- 发送请求校验入参false(失败)
6.其他校验
(1)@NotNull
- 入参不能为空,支持定义任何类型,多数用于基础类型的校验
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @NotNull private Long parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
入参不为空即可,且符合定义的类型
- 发送请求校验入参空值(失败)
- 发送请求校验入参其他值(成功)
(2)@Size
- 限制入参必须为字符串或者集合,不超过定义的范围
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Size(min = 1,max = 3,message = "字符串超过该范围") private List<String> parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@RequestBody @Valid DailyDTO dailyDTO){ return "ok"; } }
限定该集合长度范围是1-3
- 发送请求校验入参 { "parameter": ["测试1","测试2","测试3"] }(成功)
- 发送请求校验入参 { "parameter": ["测试1"] }(成功)
- 发送请求校验入参 { "parameter": ["测试1","测试2","测试3","测试4"] }(失败)
- 发送请求校验入参其他范围值(失败)
(3)@Email
- 邮箱格式校验,必须是@后跟*.*格式拼接
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Email private String parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
必须为一个邮箱的格式
- 发送请求校验入参21412421@(失败)
- 发送请求校验入参21412421@qq.(失败)
- 发送请求校验入参21412421@.com(失败)
- 发送请求校验入参21412421@qq.c(成功)
- 发送请求校验入参其他非邮箱格式(失败)
(4)@Pattern(regexp=)
- 声明的字符串需要匹配定义的正则表达式,或者为null 值
举例:
@Data public class DailyDTO implements Serializable { private static final long serialVersionUID = -513334502940807737L; @Pattern(regexp = "^[0-9]*$") private List<String> parameter; }
@RestController public class AnnotationUser { @PostMapping("/validator") public Object validator(@Valid DailyDTO dailyDTO){ return "ok"; } }
该正则表达式:必须为0-9的数字
- 发送请求校验入参45465465(成功)
- 发送请求校验入参其他非数字(失败)
下面是常用的正则表达式(注意转义符的使用,例如:“\d”要写成“\\d”):
1 匹配首尾空格的正则表达式:(^\s*)|(\s*$)
2 整数或者小数:^[0-9]+\.{0,1}[0-9]{0,2}$
3 只能输入数字:"^[0-9]*$"。
4 只能输入n位的数字:"^\d{n}$"。
5 只能输入至少n位的数字:"^\d{n,}$"。
6 只能输入m~n位的数字:。"^\d{m,n}$"
7 只能输入零和非零开头的数字:"^(0|[1-9][0-9]*)$"。
8 只能输入有两位小数的正实数:"^[0-9]+(.[0-9]{2})?$"。
9 只能输入有1~3位小数的正实数:"^[0-9]+(.[0-9]{1,3})?$"。
10 只能输入非零的正整数:"^\+?[1-9][0-9]*$"。
11 只能输入非零的负整数:"^\-[1-9][]0-9"*$。
12 只能输入长度为3的字符:"^.{3}$"。
13 只能输入由26个英文字母组成的字符串:"^[A-Za-z]+$"。
14 只能输入由26个大写英文字母组成的字符串:"^[A-Z]+$"。
15 只能输入由26个小写英文字母组成的字符串:"^[a-z]+$"。
16 只能输入由数字和26个英文字母组成的字符串:"^[A-Za-z0-9]+$"。
17 只能输入由数字、26个英文字母或者下划线组成的字符串:"^\w+$"。
18 验证用户密码:"^[a-zA-Z]\w{5,17}$"正确格式为:以字母开头,长度在6~18之间,只能包含字符、数字和下划线。
19 验证是否含有^%&',;=?$\"等字符:"[^%&',;=?$\x22]+"。
20 只能输入汉字:"^[\u4e00-\u9fa5]{0,}$"
21 验证Email地址:"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"。
22 验证InternetURL:"^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$"。
23 验证电话号码:"^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$"正确格式为:"XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX"。
24 验证身份证号(15位或18位数字):"^\d{15}|\d{18}$"。
25 验证一年的12个月:"^(0?[1-9]|1[0-2])$"正确格式为:"01"~"09"和"1"~"12"。
26 验证一个月的31天:"^((0?[1-9])|((1|2)[0-9])|30|31)$"正确格式为;"01"~"09"和"1"~"31"。
27 匹配中文字符的正则表达式: [\u4e00-\u9fa5]
28 匹配双字节字符(包括汉字在内):[^\x00-\xff]
29 应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)
30 String.prototype.len=function(){return this.replace(/[^\x00-\xff]/g,"aa").length;}
31 匹配空行的正则表达式:\n[\s| ]*\r
32 匹配html标签的正则表达式:<(.*)>(.*)<\/(.*)>|<(.*)\/>
二.高级校验
1.自定义注解校验
- 自定义注解@DailyType进行参数校验
- 参考框架内的注解编写
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented // 指定具体的校验逻辑接口 @Constraint(validatedBy = {DailyTypeEnumValidator.class }) public @interface DailyType { String message() default "参数不合法"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
- 实现ConstraintValidator接口,编写自定义注解的校验逻辑
- 举例:这里校验参数必须等于"hiber",否则抛出异常
public class DailyTypeEnumValidator implements ConstraintValidator<DailyType, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { //这里进行参数校验,举例:值必须等于“hiber”,才符合通过 if("hiber".equals(value)){ return true; } return false; } }
2.分组校验
- 先定义两个空接口:新增和更新
public interface Insert { }
public interface Update { }
- 对参数进行制定分组校验
/** 要使用分组校验的话, 需要使用 @Validated 这个注解 */ @Data public class PersonDTO { /** * id, 更新时候校验 */ @NotNull(groups = Update.class, message = "id不能为空") private Integer id; @NotBlank(groups = { Insert.class, Update.class }, message = "名字不能为空" ) private String name; @NotBlank(groups = Update.class, message = "性别不能为空") private String gender; /** * 没有指定分组, 即为默认分组 Default */ @NotNull(message = "年龄不能为空") private Integer age; }
- 需使用注解@Validated进行制定分组校验
/** * 新增分组校验测试 * @param personDTO * @return */ @PostMapping("/person") public Object groupValidationInsertDemo(@Validated(Insert.class) @RequestBody PersonDTO personDTO){ return "ok"; } /** * 更新分组校验测试 * @param personDTO * @return */ @PutMapping("/person") public Object groupValidationUpdateDemo(@Validated(Update.class) @RequestBody PersonDTO personDTO){ return "ok"; }
3.校验工具
- 自定义编写工具,一般用于service层的校验,考虑到service层不仅仅给controller层调用
public class ValidatorUtils { private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); public static <T> void validate(T object, Class... groups) { Set<ConstraintViolation<T>> validate = validator.validate(object, groups); // 如果校验结果不为空 if (!CollectionUtils.isEmpty(validate)) { StringBuilder exceptionMessage = new StringBuilder(); validate.forEach(constraintViolation -> { exceptionMessage.append(constraintViolation.getMessage()); }); throw new IllegalArgumentException(exceptionMessage.toString()); } } }
4.集合校验
- 编写一个实体,这里的age属性没有标注,可为空
@Data public class PersonDTO{ private Integer age; }
- 使用重写的ValidatorList对实体PersonDTO进行封装,请求测试
- 传递参数空列表(失败)
- 传递参数列表中的空属性(成功)
@PostMapping("/level") public Object levelValidation(@Validated @RequestBody ValidatorList<PersonDTO> personDTOList) { System.err.println(personDTOList.toString()); return "ok"; }
- 这里重写List,在校验对象的属性上使用@Valid注解实现级联校验,列表List不能为空
@Data public class ValidatorList<E> implements List<E> { @Valid @NotEmpty(message = "请求参数集合不能为空") @NotNull(message = "集合不能为NULL") private List<E> list = new ArrayList<E>(); @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 boolean equals(Object o) { return list.equals(o); } @Override public int hashCode() { return list.hashCode(); } @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); } }
三、其他校验
1.全局异常处理
- 在基础的参数校验中所产生的异常仅服务端可见
- 把该异常抛出去到前端也可见
- 添加以下的配置类(仅针对MethodArgumentNotValidException异常处理),当 @Valid 作用的参数上,校验不通过时候,抛出该异常。
- 备注: @Validated 这个注解一样也有效
@RestControllerAdvice @Configuration @Slf4j public class GlobalExceptionHandle { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Object validationExceptionHandle(MethodArgumentNotValidException mav) { log.warn("----- handle argument not valid exception! -----"); List<FieldError> fieldErrors = mav.getBindingResult().getFieldErrors(); return fieldErrors.stream().map(item -> { StringJoiner sj = new StringJoiner(":"); sj.add(item.getField()); sj.add(item.getDefaultMessage()); return sj.toString(); }).collect(Collectors.toList()); } }