背景
参考地址:https://mp.weixin.qq.com/s/lcXIl2ifr3OmiSOPdaXLnw
通过注解实现参数校验。
单个参数校验
/**
* controller 上必须加 @Validated ,同时NotNull和 RequestParam本身的非空冲突
* 加 @Validated之后,并不意味着其他类型参数校验可以不用@Validated
* @param id
* @throws Exception
*/
@ApiOperation("单个参数校验")
@GetMapping("/singleParam")
public void login(@ApiParam(value= "主键id") @Min(1) @RequestParam Integer id) throws Exception{
ResponseFactory.success();
}
一般DTO使用
DTO 定义:
public class UserDTO {
/**
* 用户名
*/
@NotNull(message = "用户名不能为空")
private String userName;
/**
* 密码
*/
@NotNull(message = "密码不能为空")
private String passWord;
}
controller层:
@PostMapping("/login")
public ResponseVo<User> login(@Valid @RequestBody UserDTO userDTO)throws Exception{
return ResponseFactory.success(userService.login(userDTO));
}
@PostMapping("/login2")
public ResponseVo<User> login2(@Validated @RequestBody UserDTO userDTO)throws Exception{
return ResponseFactory.success(userService.login(userDTO));
}
总结:这时候使用 @Valid 或 @Validated 效果一致
分组校验
起因:在实际项目中,可能多个方法需要使用同一个DTO类来接收参数,而不同方法的校验规则很可能是不一样的。
比如保存User的时候,UserId是可空的,但是更新User的时候,UserId的必须有值。
解决方案:约束注解上声明适用的分组信息groups
DTO定义:
@Data
@ApiModel("分组校验dto1")
public class UserGloupValidDTO1 {
public interface Add {
}
public interface Update {
}
/**
* 用户名
*/
@NotNull(message = "id不能为空", groups = UserGloupValidDTO1.Add.class)
@ApiModelProperty(value = "id")
private Integer id; //用int的话,@NotNull校验不行,因为它校验的是对象
/**
* 用户名
*/
@NotNull(message = "用户名不能为空", groups = {UserGloupValidDTO1.Add.class, UserGloupValidDTO1.Update.class} )
@ApiModelProperty(value = "用户名")
private String userName;
/**
* 密码
*/
@NotNull(message = "密码不能为空", groups = {UserGloupValidDTO1.Add.class, UserGloupValidDTO1.Update.class})
@ApiModelProperty(value = "密码")
private String passWord;
}
controller定义:
@ApiOperation("分组校验1")
@PostMapping("/valid3")
public void login3(@Validated(UserGloupValidDTO1.Add.class) @RequestBody UserGloupValidDTO1 reqDTO)throws Exception{
}
注:DTO中定义接口类标识分组类别,接口类除了名字,没有别的内容。与controller 层中@Validated(UserGloupValidDTO1.Add.class) 对应。说明这个只是起到一个标识的作用。因为一个项目有多个dto,如果每个都自己定义分组的话,就太多了。
定义公共校验分组 :
public class ValidGroups {
public interface Add {
}
public interface Delete {
}
public interface Query {
}
public interface Update {
}
}
DTO和controller 中分别使用这个标记{
@NotNull(message = "id不能为空", groups = ValidGroups.Update.class)
@Validated(ValidGroups.Update.class)
ps:分组校验不能用@Valid ,编译就失败了
嵌套校验
前面的示例中,DTO类里面的字段都是基本数据类型和String类型。
但是实际场景中,有可能某个字段也是一个对象,这种情况先,可以使用嵌套校验。
比如,上面保存User信息的时候同时还带有Job信息。
需要注意的是,此时DTO类的对应字段必须标记@Valid注解。
NestValidDTO.java:
@NotNull(message = "用户名不能为空", groups = {ValidGroups.Add.class, ValidGroups.Update.class})
private String userName;
@Valid //DTO类的对应字段必须标记@Valid注解
private JobDTO jobDTO;
注:当NestValidDTO.java 只剩下 jobDTO ,同时将它修改为List ,也就实现了集合校验
集合校验
参考的文章中使用的是
定义一个validList{
public class ValidationList<E> implements List<E> {
@Delegate // @Delegate是lombok注解
@Valid // 一定要加@Valid注解
public List<E> list = new ArrayList<>();
// 一定要记得重写toString方法
@Override
public String toString() {
return list.toString();
}
}
}
controller {
/**
* 多个错误只能抛出一个,同时错误为“java.lang.IllegalStateException: JSR-303 ”
* 无法被参数异常捕获
* @param userList
* @return
*/
@ApiOperation("集合-分组校验(文章示例)")
@PostMapping("/saveList6")
public ResponseVo saveList(@RequestBody @Validated(ValidGroups.Update.class) ValidationList<UserGloupValidDTO2> userList) {
// 校验通过,才会执行业务逻辑处理
return ResponseFactory.success();
}
}
总结:这种方式不行,使用嵌套校验“注”中提到的方式。
编程式校验
@Autowired
private javax.validation.Validator globalValidator;
/**
* 编程式校验
* @param userDTO
* @return
*/
@ApiOperation("编程式校验")
@PostMapping("/saveList7")
public ResponseVo saveWithCodingValidate(@RequestBody UserGloupValidDTO2 userDTO) {
Set<ConstraintViolation<UserGloupValidDTO2>> validate = globalValidator.validate(userDTO, ValidGroups.Update.class);
// 如果校验通过,validate为空;否则,validate包含未校验通过项
if (validate.isEmpty()) {
// 校验通过,才会执行业务逻辑处理
} else {
for (ConstraintViolation<UserGloupValidDTO2> userDTOConstraintViolation : validate) {
// 校验失败,做其它逻辑
System.out.println(userDTOConstraintViolation);
}
}
return ResponseFactory.success();
}
当我们并不是参数失败就退出时,需要这种方式
自定义参数校验注解
参考我的一篇注解文章