参数校验:
单个参数校验:
@RestController
@Validated
public class PingController {
@GetMapping("/getUser")
public String getUserStr(@NotNull(message = "name 不能为空") String name,
@Max(value = 99, message = "不能大于99岁") Integer age) {
return "name: " + name + " ,age:" + age;
}
}
当处理GET请求时或只传入少量参数的时候,我们可能不会建一个bean来接收这些参数,就可以像上面这样直接在controller方法的参数中进行校验。
注意:这里一定要在方法所在的controller类上加入@Validated注解,不然没有任何效果。
这时候在postman输入请求:
http://localhost:8080/getUser?name=Allan&age=101
调用方会收到springboot默认的格式报错:
{
"timestamp": "2019-06-01T04:30:26.882+0000",
"status": 500,
"error": "Internal Server Error",
"message": "getUserStr.age: 不能大于99岁",
"path": "/getUser"
}
实体类参数校验:
当处理post请求或者请求参数较多的时候我们一般会选择使用一个bean来接收参数,然后在每个需要校验的属性上使用参数校验注解:
public class UserInfo {
@NotNull(message = "username cannot be null")
private String name;
@NotNull(message = "sex cannot be null")
private String sex;
@Max(value = 99L)
private Integer age;
}
然后在controller方法中用@RequestBody表示这个参数接收的类:
@RestController
public class PingController {
@GetMapping("metrics/ping")
public Response<String> ping() {
return new Response<>(ResponseCode.SUCCESS, null,"pang");
}
@PostMapping("/getUser")
public String getUserStr(@RequestBody @Validated({GroupA.class, Default.class}) UserInfo user, BindingResult bindingResult) {
validData(bindingResult);
return "name: " + user.getName() + ", age:" + user.getAge();
}
private void validData(BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
StringBuffer sb = new StringBuffer();
for (ObjectError error : bindingResult.getAllErrors()) {
sb.append(error.getDefaultMessage());
}
throw new ValidationException(sb.toString());
}
}
}
需要注意的是,如果想让UserInfo中的参数注解生效,还必须使用@Validated注解修饰DTO参数。这种参数校验方式的校验结果会被放到BindingResult中,我们这里写了一个统一的方法来处理这些结果,通过抛出异常的方式得到GlobalExceptionHandler(详情查看控制器增强器)的统一处理。
校验模式:
DTO实体校验字段,通常按顺序验证到第一个字段不符合验证要求时,就可以直接拒绝请求了。这就涉及到两种校验模式的配置:
1,普通模式(默认是这个模式): 会校验完所有的属性,然后返回所有的验证失败信息。
2,快速失败模式: 只要有一个验证失败,则返回。
如果想要配置第二种模式,需要添加如下配置类:
@Configuration
public class ValidatorConf {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
参数校验分组:
在实际开发中经常会遇到这种情况:想要用一个实体类去接收多个controller的参数,但是不同controller所需要的参数又有些许不同,而你又不想为这点不同去建个新的类接收参数。比如有一个/setUser接口不需要id参数,而/getUser接口又需要该参数,这种时候就可以使用参数分组来实现。
定义表示组别的interface;
public interface GroupA {
}
在@Validated中指定使用哪个组;
@RestController
public class PingController {
@PostMapping("/getUser")
public String getUserStr(@RequestBody @Validated({GroupA.class, Default.class}) UserInfo user, BindingResult bindingResult) {
validData(bindingResult);
return "name: " + user.getName() + ", age:" + user.getAge();
}
@PostMapping("/setUser")
public String setUser(@RequestBody @Validated UserInfo user, BindingResult bindingResult) {
validData(bindingResult);
return "name: " + user.getName() + ", age:" + user.getAge();
}
}
其中Default为javax.validation.groups中的类,默认分组,表示DTO参数类中,其他没有参与分组的参数。如果没有指定Default.class,/getUser接口仅仅校验标记了GroupA的分组的参数。
在实体类的注解中标记这个哪个组所使用的参数;
@Data
public class UserInfo {
@NotNull( groups = {GroupA.class}, message = "id cannot be null")
private Integer id;
@NotNull(message = "username cannot be null")
private String name;
@NotNull(message = "sex cannot be null")
private String sex;
@Max(value = 99L)
private Integer age;
}
级联参数校验:
当参数bean中的属性又是一个复杂数据类型或者是一个集合的时候,如果需要对其进行进一步的校验需要考虑哪些情况呢?
@Data
public class UserInfo {
@NotNull( groups = {GroupA.class}, message = "id cannot be null")
private Integer id;
@NotNull(message = "username cannot be null")
private String name;
@NotNull(message = "sex cannot be null")
private String sex;
@Max(value = 99L)
private Integer age;
@NotEmpty
private List<Parent> parents;
}
比如对于parents参数,@NotEmpty只能保证list不为空,但是list中的元素是否为空、User对象中的属性是否合格,还需要进一步的校验。这个时候我们可以这样写:
@NotEmpty
private List<@NotNull @Valid UserInfo> parents;
然后再继续在UserInfo类中使用注解对每个参数进行校验。
但是我们再回过头来看看,在controller中对实体类进行校验的时候使用的@Validated,关于这两个注解的具体区别可以参考@Valid 和@Validated的关系,但是在这里我想说的是使用@Valid就没办法对UserInfo进行分组校验。
注解:
@Valid:
修饰的地方开启验证。
@Validated:
Validated是Valid 的一次封装。扩展功能有:
1、分组。参照:参数校验分组。
2、组序列。参照@GroupSequence注解。
@Min(value):
修饰数字类型,不能小于指定值。
@Max:
修饰数字类型,不能大于指定值。
@Length(value):
修饰字符串,长度不能超过指定值。
@NotNull:
不能为null。
@NotEmpty:
修饰集合。不能为Null,不能为空集合。
@NotBlank:
修饰字符串:不能为null,不能为只有空格类型的字符串。