SpringBoot接口参数验证,使用@Validated实现分组验证

2,172 阅读4分钟

SpringBoot提供了参数校验注解@Validated,用于标记参数实体成员属性以进行验证,虽然@Valid也可以实现属性的验证,但是@Valid不支持分组级的验证。所谓分组验证是指对于不同的请求共用一个请求参数体时,由于这些不同的请求参数的校验规则是不同的,导致同一参数实体需要多个验证规则,这个时候就需要将验证规则分组来满足不同的参数验证需求。@Validated就提供了这一功能。

使用@Validated实现参数验证

引入validation依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

创建一个参数实体类

@Data
public class UserInfoDTO {
    @NotNull(message = "用户姓名不能为空")
    private String userName;
    @NotNull(message = "年龄不能为空")
    @Min(value = 18,message = "年龄不能小于18")
    @Max(value = 100,message = "年龄不能超过100")
    private Integer age;
}

提示
上面的参数验证注解需要引入包:import javax.validation.constraints.*;

  • @NotNull表示参数不能为空
  • @Min表示数值的最小值
  • @Max表示数值的最大值
  • message属性用来设置验证失败的提示信息

创建一个接口测试参数验证

@RestController
@RequestMapping("/valid")
public class ValidatedController {
    @PostMapping("/user")
    public UserInfoDTO validUserInfo(@Validated @RequestBody UserInfoDTO param){
        return param;
    }
}

提示

  1. @Validated直接写在请求的参数之前
  2. 需要使用@RequestBody配合@Validated使用,将json请求体映射到实体类中

为了能更直观的看到参数校验的结果,这里自定义一个异常,用来返回参数验证失败的异常信息

创建自定义异常

@RestControllerAdvice
public class CommonException {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> validated(MethodArgumentNotValidException ex){
        return ResponseEntity.of(Optional.of(ex.getFieldError().getDefaultMessage()));
    }
}
  • MethodArgumentNotValidException是参数验证失败时抛出的异常
  • ex.getFieldError().getDefaultMessage()是具体的参数异常提示信息
  • ResponseEntityspringboot内置的响应体

使用postman进行参数验证测试

image.png

什么都不传返回自定义的提示:'userName不能为空'。这个是按照实体中参数的顺序来返回的,第一个参数不符合条件直接返回。

image.png

只传一个userName参数则返回提示:'年龄不能为空'

image.png

如果是请求的参数不符合属性的验证规则,同样会返回错误信息。

image.png

如果传递了正确的信息,则成功返回请求的参数

使用@Validated实现参数分组验证

刚才的代码举例说明了@Validated的参数验证功能,但是日常的业务场景中很可能存在不同的请求使用或传递同一个参数实体类型,比如现在需要传递未成年人和成年人两种用户信息,如果是未成年人,那么传递的年龄信息就要小于18岁,如果是成年人,年龄则要大于等于18岁。这个时候就要使用分组验证。

创建两个接口类,分别表示成年人组和未成年人组

/**
 * 成年人
 */
public interface Adult {
}
/**
 * 未成年人
 */
public interface Juveniles {
}

修改UserInfoDTO实体的校验规则

@Data
public class UserInfoDTO {
    @NotNull(message = "用户姓名不能为空")
    private String userName;
    @NotNull(message = "年龄不能为空")
    @Min(value = 18,message = "年龄不能小于18",groups = Adult.class)
    @Max(value = 100,message = "年龄不能超过100",groups = Adult.class)
    @Max(value = 17,message = "年龄不能大于17岁",groups = Juveniles.class)
    private Integer age;
}

提示
主要的修改是在校验注解中添加了groups属性,用来指定当前的校验针对哪一个组。
@Max(value = 100,message = "年龄不能超过100",groups = Adult.class)@Min(value = 18,message = "年龄不能小于18",groups = Adult.class)指定了成年人用户信息的年龄属性验证规则。
@Max(value = 17,message = "年龄不能大于17岁",groups = Juveniles.class)指定了未成年人用户信息的年龄要小于18岁。

创建两个测试接口,分别传递成年人和未成年人用户信息

/**
 * 成年人
 * @param param
 * @return
 */
@PostMapping("/userAdult")
public UserInfoDTO validUserAdult(@Validated(value = Adult.class) @RequestBody UserInfoDTO param){
    return param;
}

/**
 * 未成年人
 * @param param
 * @return
 */
@PostMapping("/userJuveniles")
public UserInfoDTO validUserJuveniles(@Validated(value = Juveniles.class) @RequestBody UserInfoDTO param){
    return param;
}

提示
@Validated(value = Adult.class)定义当前请求参数的验证规则属于哪一个组,比如当前的请求参数适用于成年人的用户信息验证规则

使用postman进行参数分组验证测试

首先是未成年用户的请求 image.png

未成年用户信息的年龄不能大于17岁

其次是成年用户的请求 image.png

成年人用户信息的年龄不能小于18岁

修改为符合成年人用户信息验证规则的参数重新请求 image.png

修改成符合成年人规则的请求参数后重新发起请求后,成功返回用户信息,表示符合验证规则请求成功。

需要注意的是,@Validated需要配合@RequestBody注解和POST请求方式来实现,传递的参数要求是json格式,否则无法实现从请求参数体到实体类的映射。