@Validated和@Valid的进阶使用

分组校验

@Validated注解提供了分组校验的机制,可以在参数验证时,根据不同的分组采用不同的验证机制。
@valid注解则没有分组校验的功能;

要使用@validated的分组校验机制,大致分为三个步骤:

  1. 定义分组接口
  2. 定义需要校验的bean
  3. 应用分组,进行测试
    下面让我们一起简单看看分组校验的用法;

定义分组接口

首先,我们自定义一个注解,作为分组接口(也可以使用现有接口)

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集合来接收参数

好了,今天就先到这里了,拜拜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值