SpringBoot常见的参数校验
通俗来说:
@NotEmpty 用在集合类上面
@NotBlank 用在String上面
@NotNull 用在基本类型上面
一般我们在开发中 对于一些字段的校验通过if else进行判空,当然这样也不是不可以,但是一旦参数多了的话,那么校验行数代码可就大了,因此采用validator通过注解的方式进行校验参数,也提高了代码的优美性!
什么是Validator
Bean Validation是Java定义的一套基于注解的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本 。
在SpringBoot中已经集成在 starter-web中,所以无需在添加其他依赖。
内置注解有这些:
空值检查
注解 | 功能 |
---|---|
@Null | 元素必须为空 |
@NotNull | 元素不能为空 |
@NotEmpty | 元素不能为null或空 |
@NotBlank | 校验约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格 |
Boolean检查
注解 | 功能 |
---|---|
@AssertTrue | 被注释的元素为true |
@AssertFalse | 被注释的元素为false |
长度检查
注解 | 功能 |
---|---|
@Length | (String)长度在范围内 |
@Size | (Array,Colleation,Map,String)长度在范围内 |
@Range | 被注释的元素必须在合适的范围内 |
数值检查
注解 | 功能 |
---|---|
@Min(value=“”,message=“”) | 被注释的元素必须是个数字并且其最小值是这个value值,如果小于的话,会抛出message信息内容 |
@Max(value=“”,message =“”) | 被注释的元素必须是个数字并且其最大值是这个value值,如果大于的话,会抛出message信息内容 |
@DecimalMin | 被注释的元素必须是个数字并且其值必须大于等于指定的值 |
@DecimalMax | 被注释的元素必须是个数字并且其值必须小于等于指定的值 |
@Digits | 验证 Number 和 String 的构成是否合法 |
@Digits (integer, fraction) | 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度 |
时间检查
注解 | 功能 |
---|---|
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
地址 正则检查
注解 | 功能 |
---|---|
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
@URL | 被注释的元素必须是个URL地址 |
指定的元素必须是电子格式 |
注意:
- @NotNull 适用于任何类型被注解的元素必须不能与NULL
- @NotEmpty 适用于String Map或者数组不能为Null且长度必须大于0
- @NotBlank 只能用于String上面 不能为null,调用trim()后,长度必须大于0
实例:
@Data
@ApiModel("注解参数校验")
public class UserRequestParam implements Serializable {
@NotNull(message = "用户名字不能notnull")
@NotBlank(message = "用户名字不能not Blank")
private String username;
@Email(message = "email格式错误")
private String email;
@NotNull(message = "性别不能为空")
private Integer sex;
}
接口处需要有@Valid 或者 @Validated注解校验
@ApiOperation("注解测试")
@PostMapping("test-validate")
public Result<Boolean> checkUser(@ApiParam @Valid @RequestBody UserRequestParam request){
return ResultBuilder.success(true);
}
全局异常处理的地方需要加个参数校验输出
/**
* 方法参数校验
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e);
return ResultBuilder.error(e.getBindingResult().getFieldError().getDefaultMessage());
}
启动项目 swagger测试接口
测试1: 任何参数都不传,因此username名字报了 @NotBlank的错误
测试2: email随便填写一个值
得到的结果就是一个不合法的电子邮件地址! 注意:如果email传空,不会报错的,只有传了值不匹配电子格式会抛出来。
测试3: 性别为Integer不传值
抛出异常,性别不能为空!
测试4:测试电话号码
@Pattern(regexp ="^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String telephone;
电话号码输入不正常抛出异常!
自定义注解
上面都是内置注解,现在可以采用自定义注解的校验
首先添加一个IdCard注解
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy =IdCardValidator.class)
public @interface IdCard {
String message() default "身份证号码输入不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
这里重点来介绍一下这几个注解的作用:
@Documented 是元注解,可以修饰其他注解,被其标注的类在生成文档的时候,会显示该类的内容。
@Target Target翻译中文为目标,即该注解可以声明在哪些目标元素之前,也可理解为注释类型的程序元素的种类。
有几种修饰类型:
- ElementType.PACKAGE:该注解只能声明在一个包名前。
- ElementType.ANNOTATION_TYPE:该注解只能声明在一个注解类型前。
- ElementType.TYPE:该注解只能声明在一个类前。
- ElementType.CONSTRUCTOR:该注解只能声明在一个类的构造方法前。
- ElementType.LOCAL_VARIABLE:该注解只能声明在一个局部变量前。
- ElementType.METHOD:该注解只能声明在一个类的方法前。
- ElementType.PARAMETER:该注解只能声明在一个方法参数前。
- ElementType.FIELD:该注解只能声明在一个类的字段前。
@Retention :Retention 翻译成中文为保留,可以理解为如何保留,即告诉编译程序如何处理,也可理解为注解类的生命周期。
-
RetentionPolicy.SOURCE : 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
-
RetentionPolicy.CLASS : 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
-
RetentionPolicy.RUNTIME : 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。
@Constraint 用来限定自定义注解的地方,找到后面的方法去实现该注解的作用。
创建IdCardValidator类
public class IdCardValidator implements ConstraintValidator<IdCard,Object> {
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
return IdcardUtil.isValidCard(o.toString());
}
@Override
public void initialize(IdCard constraintAnnotation) {
}
}
这里直接采用Hutool工具包里IdCardUtil工具类检测身份证号是否正常
测试1:
先添加字段和对应的注解
@IdCard
private String idCard;
随便输入一个身份证号,值异常!
测试2: 输入一个正确的身份证号,未有遗产抛出
group分组
分组校验指的是 一个请求参数可能被多个接口入参所使用,但是每个接口涉及入参的校验信息不完全一致,因为采用分组校验入参信息,只针对该分组的参数信息进行校验!
上代码
创建两个接口分组名称
public interface Create extends Default {
}
public interface Update extends Default {
}
这个入参做了下改动 ,其他的入参忽略掉
@NotNull(message = "用户名字不能notnull",groups = Create.class)
@NotBlank(message = "用户名字不能not Blank")
private String username;
@Email
@JsonIgnore
private String email;
@NotNull(message = "性别不能为空",groups = Update.class)
private Integer sex;
@Pattern(regexp ="^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
@JsonIgnore
private String telephone;
@JsonIgnore
private String idCard;
接口处:
public Result<Boolean> checkUser(@Validated(Create.class) @RequestBody UserRequestParam request){
return ResultBuilder.success(true);
}
测试1: 名字为空传参 但是性别不为空
得出结果!
测试2 : 名字正常传,但是性别不传
得到以上结果,结果正常!
测试3: 接口处改下校验分组
得到结果如你所愿
以上是我个人心得体会!
文档借鉴了该文档部分说明
https://blog.youkuaiyun.com/adparking/article/details/112918333