开发过程中,参数的校验是必不可少的。前端的校验是为了用户体验,后端的校验是为了安全。试想一下,如果在controller层中没有经过任何校验的参数通过service层,dao层一路传到了数据库就可能导致严重的后果,或许查不出数据,若包含了恶意的代码,那就可能导致更严重的后果。
还记得在最初接触Hibernate时,感慨框架功能的强大,其中就有通过配置文件便可以实现自定义参数的校验,那么功能强大的Spring是不是也可以呢?答案是肯定的。
Hibernate Validator:
https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#preface
Spring Validator:
https://docs.spring.io/spring/docs/5.0.5.RELEASE/spring-framework-reference/core.html#validation
首先介绍下在springBoot中的几种参数校验方式。常用于参数校验的注解如下:
- @AssertFalse 所注解的元素必须是Boolean类型,且值为false
- @AssertTrue 所注解的元素必须是Boolean类型,且值为true
- @DecimalMax 所注解的元素必须是数字,且值小于等于给定的值
- @DecimalMin 所注解的元素必须是数字,且值大于等于给定的值
- @Digits 所注解的元素必须是数字,且值必须是指定的位数
- @Future 所注解的元素必须是将来某个日期
- @Max 所注解的元素必须是数字,且值小于等于给定的值
- @Min 所注解的元素必须是数字,且值小于等于给定的值
- @Range 所注解的元素需在指定范围区间内
- @NotNull 所注解的元素值不能为null
- @NotBlank 所注解的元素值有内容
- @Null 所注解的元素值为null
- @Past 所注解的元素必须是某个过去的日期
- @PastOrPresent 所注解的元素必须是过去某个或现在日期
- @Pattern 所注解的元素必须满足给定的正则表达式
- @Size 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内
- @Email 所注解的元素需满足Email格式
@NotNull @NotEmpty @NotBlank 3个注解的区别:
@NotNull 任何对象的value不能为null
@NotEmpty 集合对象的元素不为0,即集合不为空,也可以用于字符串不为null
@NotBlank 只能用于字符串不为null,并且字符串trim()以后length要大于0
示例:
1.引入 spring-boot-starter-validation依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.controller中开启参数校验
2.1单个参数校验
@RestController
@Validated
public class TestController {
@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注解,不然没有任何效果。
2.2实体类参数校验
@Data
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 {
@Autowired
private Validator validator;
@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中的参数注解生效,必须在controller参数汇总使用@Validated注解。
参数校验分组
在实际开发中经常会用到这种情况:想要一个实体类去接收多个controller的参数,但是不同的controller所需要的参数又有些不同,而我们又不想为这点不同去建一个新的类接收参数。比如有一个/setUser接口不需要id参数,而 /getUser接口又需要该参数,这种时候就可以使用参数分组来实现。
1.定义标识分组别的interface;
public interface GroupA {
}
2.在@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中的类,表示参数类中其他没有分组的参数。如果没有,/getUser接口的参数校验就只会有标记了GroupA的参数校验生效。
3.在实体类的注解中标记哪个组所使用的参数
@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中的属性又是一个复杂数据类型或者一个集合的时候,需要对其进行进一步的校验。还可以进行自定义的参数校验。这里就不一一进行阐述了。纸上得来终觉浅,绝知此事要躬行。重在实践。希望对看到的小伙伴有所帮助。