分组校验
@Validated注解提供了分组校验的机制,可以在参数验证时,根据不同的分组采用不同的验证机制。
@valid注解则没有分组校验的功能;
要使用@validated的分组校验机制,大致分为三个步骤:
- 定义分组接口
- 定义需要校验的bean
- 应用分组,进行测试
下面让我们一起简单看看分组校验的用法;
定义分组接口
首先,我们自定义一个注解,作为分组接口(也可以使用现有接口)
public @interface GroupA {
}
public @interface GroupB {
}
定义需要校验的bean
@Data
public class GroupTestDTO {
@NotNull(message = "不能为空")
private Object notNull;
@Length(min = 1, max = 10, message = "长度在1到10之间", groups = {GroupA.class})
private String length;
@Min(value = 1, message = "最小是1", groups = {GroupB.class})
private Integer min;
@NotBlank(message = "不能为空字符串", groups = {GroupA.class,GroupB.class})
private String notBlank;
@Email(message = "邮箱格式错误", groups = {GroupA.class,GroupB.class, Default.class})
private String email;
}
@validated注解指定校验分组
@PostMapping("/requestBodyValidDefault")
public GroupTestDTO requestBodyValidDefault(@RequestBody @Validated GroupTestDTO dto){
return dto;
}
@PostMapping("/requestBodyValidGroupATest")
public GroupTestDTO requestBodyValidGroupATest(@RequestBody @Validated({GroupA.class}) GroupTestDTO dto){
return dto;
}
@PostMapping("/requestBodyValidGroupBTest")
public GroupTestDTO requestBodyValidGroupBTest(@RequestBody @Validated({GroupB.class}) GroupTestDTO dto){
return dto;
}
@PostMapping("/requestBodyValidGroupABTest")
public GroupTestDTO requestBodyValidGroupABTest(@RequestBody @Validated({GroupA.class,GroupB.class}) GroupTestDTO dto){
return dto;
}
@PostMapping("/requestBodyValidGroupABDefaultTest")
public GroupTestDTO requestBodyValidGroupABDefaultTest(@RequestBody @Validated({GroupA.class,GroupB.class, Default.class}) GroupTestDTO dto){
return dto;
}
测试
组序列校验
默认情况下 不同级别的约束验证是无序的,但是在一些情况下,顺序验证却是很重要;
一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。
定义组序列
@GroupSequence({Default.class, GroupA.class, GroupB.class})
public @interface GroupSequence1 {
}
定义需要校验的bean
@Data
public class GroupSequenceDTO {
@NotNull(message = "不能为空")
private Object notNull;
@Length(min = 1, max = 10, message = "长度在1到10之间", groups = {GroupA.class})
private String length;
@Min(value = 1, message = "最小是1", groups = {GroupB.class})
private Integer min;
}
指定分组序列
@PostMapping("/requestBodyValidGroupSequenceTest")
public GroupSequenceDTO requestBodyValidGroupSequenceTest(@RequestBody @Validated({GroupSequence1.class}) GroupSequenceDTO dto){
return dto;
}
测试
嵌套校验
注意:validated注解不支持嵌套校验,所以需要valid注解来配合完成嵌套校验
在嵌套校验时,需要将valid注解放置在需要校验的属性上;
这里先不使用valid注解,来看看效果:
测试代码如下:
@Data
public class TeacherDTO {
@NotBlank(message = "老师名称不能为空")
private String name;
@Range(min = 20, max = 100, message = "老师年龄必须在20到100之间")
private Integer age;
}
@Data
public class StudentDTO {
@NotBlank(message = "学生名称不能为空")
private String name;
@Range(min = 20, max = 100, message = "学生年龄必须在6到30之间")
private Integer age;
@NotNull(message = "学生的老师列表不能为空")
@Size(min = 1, message = "学生至少需要一名老师")
private List<TeacherDTO> teacherList;
}
@PostMapping("/requestBodyValidNestTest")
public StudentDTO requestBodyValidNestTest(@RequestBody @Validated StudentDTO dto){
return dto;
}
此时调用接口:
可以看到,老师的名称和年龄同样不满足,但是validated并没有校验出来;
下面我们给teacherList属性添加@valid注解,来开启嵌套校验:
@Data
public class StudentDTO {
@NotBlank(message = "学生名称不能为空")
private String name;
@Range(min = 20, max = 100, message = "学生年龄必须在6到30之间")
private Integer age;
@Valid
@NotNull(message = "学生的老师列表不能为空")
@Size(min = 1, message = "学生至少需要一名老师")
private List<TeacherDTO> teacherList;
}
再次调用接口,如下:
基础校验补充
之前的例子都是基于post请求,requestbody形式的参数来进行校验,这里我们一起看看get请求中requestParam/PathVariable参数校验
在进行get请求中requestParam/PathVariable参数校验,需要在对应的controller类上添加@Validated注解,如下所示:
@Validated
@RestController
@RequestMapping("/hello")
public class HelloController {
@GetMapping("/requestBodyValidPathTest/{length}")
public GroupSequenceDTO requestBodyValidPathTest(@PathVariable("length") @Length(min = 1, max = 10, message = "长度在1到10之间") String length){
return new GroupSequenceDTO()
.setLength(length)
;
}
@GetMapping("/requestBodyValidParamTest")
public GroupSequenceDTO requestBodyValidParamTest(@Length(min = 1, max = 10, message = "长度在1到10之间") String length){
return new GroupSequenceDTO()
.setLength(length)
;
}
}
接下来我们调用接口,看看效果:
发现返回的异常信息并没有我们预想的友好化展示,即我们之前定义的异常统一处理器失效了,查看后台日志如下:
发现,在get请求中requestParam/PathVariable参数校验时,如果校验失败,validator抛出的异常是ConstraintViolationException,而不是我们之前定义的MethodArgumentNotValidException。
所以,我们需要继续改造异常处理器,如下所示:
@RestControllerAdvice
public class ExceptionHandler {
@org.springframework.web.bind.annotation.ExceptionHandler({MethodArgumentNotValidException.class})
@ResponseStatus(HttpStatus.OK)
public ResultVO validExceptionHandle(MethodArgumentNotValidException exception){
BindingResult bindingResult = exception.getBindingResult();
StringBuilder errorMsg = new StringBuilder("校验失败:");
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errorMsg.append(fieldError.getDefaultMessage()).append(";");
}
return new ResultVO()
.setCode(ResultVO.VALID_NOT_PASS)
.setMsg(errorMsg.toString())
;
}
@org.springframework.web.bind.annotation.ExceptionHandler({ConstraintViolationException.class})
@ResponseStatus(HttpStatus.OK)
public ResultVO handleConstraintViolationException(ConstraintViolationException ex) {
return new ResultVO()
.setCode(ResultVO.VALID_NOT_PASS)
.setMsg("校验失败:" + ex.getMessage())
;
}
}
再次调用接口,如下所示:
注意:
如果请求体直接传递了json数组给后台,并希望对数组中的每一项都进行参数校验。此时,如果我们直接使用java.util.Collection下的list或者set来接收数据,参数校验并不会生效!我们可以使用自定义list集合来接收参数
好了,今天就先到这里了,拜拜