Springboot validator参数校验快速入门
一、引用Jar
<!--引入spring-boot-starter-validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.1.Final</version>
</dependency>
二、PathVariable校验
路径变量使用正则表达式。当请求URI不满足正则表达式时,客户端将收到404错误码。不方便的地方是,不能通过捕获异常的方式,向前端返回统一的、自定义格式的响应参数。
@GetMapping("/path/{group:[a-zA-Z0-9_]+}/{userid}")
@ResponseBody
public String path(@PathVariable("group") String group, @PathVariable("userid") Integer userid) {
return group + ":" + userid;
}
三、方法参数校验
如果前端传递的参数不满足规则,则抛出异常。注解Size、Min、Max来自validation-api.jar
@GetMapping("/validate1")
@ResponseBody
public String validate1(
@Size(min = 1,max = 10,message = "姓名长度必须为1到10")@RequestParam("name") String name,
@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100") @RequestParam("age") Integer age) {
return "validate1";
}
四、表单对象/VO对象校验
当参数是VO时,可以在VO类的属性上添加校验注解。
public class LoginInfo {
// 用户标识
@NotBlank(message = "用户登录名称不能为空")
private String loginUser;
// 用户角色
@NotNull(message = "用户角色不能为空")
@Size(min=1,message="用户角色不能为空")
private List<String> loginRole;
// 企业信息
@Valid
private OrgInfo orgInfo;
}
public class OrgInfo {
// 单位或企业的名称
@NotBlank(message = "单位或企业的名称不能为空")
private String orgName;
// 企业的统一社会信用代码,政府部门可填写其他的唯一标识
@NotBlank(message = "企业的统一社会信用代码不能为空")
private String orgCode;
// 企业类型
@Min(value=0L,message = "企业类型不能小于0")
private Integer orgType;
}
五、使用@Valid注解开启校验
5.1 默认方式
在Controller方法参数前加@Valid注解——校验不通过时直接抛异常
@LogInfomation
@PostMapping("/test/v")
@ResponseBody
public ResponseData<Boolean> testV( @RequestBody @Valid LoginInfo loginInfo) {
System.out.println(loginInfo.toJsonStr());
return ResponseData.ok(Boolean.TRUE);
}
校验失败会抛出org.springframework.web.bind.MethodArgumentNotValidException
异常,具体信息如下:
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.baec.smear.lib.model.ResponseData<java.lang.Boolean> com.baec.smear.ui.controller.LoginController.testV(com.baec.smear.lib.sso.dto.LoginInfo): [Field error in object 'loginInfo' on field 'loginUser': rejected value []; codes [NotBlank.loginInfo.loginUser,NotBlank.loginUser,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [loginInfo.loginUser,loginUser]; arguments []; default message [loginUser]]; default message [用户登录名称不能为空]]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:138) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
。。。。
5.2 进阶版
在Controller
方法参数前加@Valid
注解,参数后面定义一个BindingResult
类型参数——执行时会将校验结果放进bindingResult
里面,用户自行判断并处理,这里直接抛出IllegalArgumentException
异常,交由全局异常处理类处理
import javax.validation.Valid;
import org.springframework.validation.BindingResult;
@LogInfomation
@PostMapping("/test/v")
@ResponseBody
public ResponseData<Boolean> testV( @RequestBody @Valid LoginInfo loginInfo, BindingResult bindingResult) {
// 参数校验
if (bindingResult.hasErrors()) {
// 每次只获取第一个异常信息
String messages = bindingResult.getAllErrors().get(0).getDefaultMessage();
throw new IllegalArgumentException(messages);
}
System.out.println(loginInfo.toJsonStr());
return ResponseData.ok(Boolean.TRUE);
}
5.3 通过API执行校验
// 关键代码
// Validator线程安全
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<LoginInfo>> result = validator.validate(loginInfo);
if(result.size() > 0) {
ConstraintViolation<LoginInfo> cv = result.stream().findFirst().get();
System.out.println(cv.getMessage());
throw new IllegalArgumentException(cv.getMessage());
}
5.4 分组校验
在实际开发中,难免遇到对一个实体类的一些属性,不同的请求会有不同的校验规则的场景,比如:
用户注册时,User类的id可以为空,但是在修改用户密码时,又需要传入Id来作为where语句的条件去更新,此时分组校验就派上用场了。
-
JavaBean
public class User { // ResetPassword分组下不能为空 // Register分组下可以为空 @NotBlank(groups = ValidationGroups.ResetPassword.class) @Null(groups = ValidationGroups.Register.class) private int id; private String userName; private int gender; private int age; }
-
定义分组
通过注解中
groups
属性设置该校验注解的分组,groups
的值要求必须是一个类对象,一般可以通过创建多个空接口来进行分组,如果分组较多可以将这些用以校验分组的接口放在同一个类下进行管理public class ValidationGroups { public interface Register { } public interface ResetPassword { } }
-
Controller中使用@Validated注解
通过
@Validated
注解指定分组@RequestMapping public String register(@Validated(ValidationGroups.Register.class) @RequestBody User user) { // 处理逻辑... return null; }
-
复杂分组
如果分类较为复杂可以通过接口间的继承来减少需要创建的接口数量,比如不论注册时还是重新设置密码,系统都要求传入的参数要有
username
,那么可以实现一个让Register
和ResetPassword
继承一个新的接口,假设该接口名为CommonUser
:public class ValidationGroups { public interface CommonUser { } public interface Register extends CommonUser{ } public interface ResetPassword extends CommonUser{ } }
然后在
User
的实体类中,在username
属性上用CommonUser
作为分组标记,由于Register
和RestPassword
都继承自CommonUser
,所以Controller
层中的Validated
注解使用的分组标记不需要改变:public class User { @NotBlank(groups = ValidationGroups.ResetPassword.class) @Null(groups = ValidationGroups.Register.class) private int id; @NotBlank(groups = ValidationGroups.CommonUser.class) private String userName; private int gender; private int age; }
当在
Controller
接口中指定ResetPassword
或者Register
分组时,作为基类的CommonUser
分组都会校验
六、相关标准
JSR 303 是Bean验证的规范 ,Hibernate Validator 是该规范的参考实现,它除了实现规范要求的注解外,还额外实现了一些注解。
validation-api-1.1.0.jar 包括如下约束注解:
约束注解 | 说明 |
---|---|
@AssertFalse | 被注释的元素必须为 false |
@AssertTrue | 被注释的元素必须为 true |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
hibernate-validator-5.3.6.jar 包括如下约束注解:
约束注解 | 说明 |
---|---|
被注释的元素必须是电子邮箱地址 | |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotBlank | 被注释的字符串的必须非空 |
@NotEmpty | 被注释的字符串、集合、Map、数组必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
@SafeHtml | 被注释的元素必须是安全Html |
@URL | 被注释的元素必须是有效URL |