参考博客:
SpringBoot 参数校验的方法www.cnblogs.com实现整理如下:
一、为什么要进行参数校验?
虽然在前端做校验,可以通过前端禁止输入的方法来使参数合法。但应该考虑到可以跳过前端,直接像后端发送请求,如果后端未做参数校验的话,会使未经校验的参数经过service层来到dao层直到数据库,造成十分严重的后果。最好的结果是查不出数据,严重一点就是报错,如果这些没有被校验的参数中包含了恶意代码,那就可能导致更严重的后果。
前端做参数校验是为了用户体验,后端做参数校验是为了安全
二、SpringBoot的参数校验
controller层参数校验流程如下:
SpringBoot的参数校验可分为两类:单个参数校验、实体类参数校验
创建SpringBoot项目CheckOut,参数校验依赖于:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
首先将常用校验注解罗列如下,以便查看:
注解 | 说明 |
---|---|
@AssertFalse | 所注解的元素必须是Boolean类型,且值为false |
@AssertTrue | 所注解的元素必须是Boolean类型,且值为true |
@DecimalMax | 所注解的元素必须是数字,且值小于等于给定的值 |
@DecimalMin | 所注解的元素必须是数字,且值大于等于给定的值 |
@Digits | 所注解的元素必须是数字,且值必须是指定的位数 |
@Future | 所注解的元素必须是将来某个日期 |
@Max | 所注解的元素必须是数字,且值小于等于给定的值 |
@Min | 所注解的元素必须是数字,且值小于等于给定的值 |
@Range | 所注解的元素需在指定范围区间内 |
@NotNul | 所注解的元素值不能为null(注意:postman测试时,无法发送null的参数 |
@NotBlank | 所注解的元素值有内容 |
@Null | 所注解的元素值为null |
@Past | 所注解的元素必须是某个过去的日期 |
@PastOrPresent | 所注解的元素必须是过去某个或现在日期 |
@Pattern | 所注解的元素必须满足给定的正则表达式 |
@Size | 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内 |
所注解的元素需满足Email格式 |
1.单个参数校验
新建CheckOutController:
@RestController
@Validated
public class CheckOutController {
@GetMapping("/single-check/test1")
public String singleCheckTest1(@NotNull(message = "name 不能为空") String name,
@Max(value = 99, message = "不能大于99岁") Integer age) {
System.out.println("参数校验通过");
return "通过";
}
}
用postman发送请求时:
参数:
name | 张三 |
age | 100 |
后台打印错误:
2. 实体类参数校验
当传递参数较多时,可以选择新建一个DTO类,来接收参数。
新建UserDTO类:
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
@NotBlank(message = "name不能为空字符串")
private String name;
@Size(max = 6, min = 2, message = "password所含字符数在[2,6]的范围内")
private String password;
@Max(value = 100, message = "age不能超过100")
private Integer age;
@AssertTrue(message = "liveInEarth必须为true")
private boolean liveInEarth;
}
这里使用@NotBlank(检验字符串不为“”)、@Size(校验字符串、数组、集合的长度范围)、@Max(校验整数值的最大值上限)、@AssertTrue(校验入参一定为true)
对应controller方法:
@RestController
@Validated
public class CheckOutController {
@PostMapping("/bean-check/test1")
public void beanCheckTest1(@RequestBody @Valid UserDTO userDTO, BindingResult bindingResult) {
System.out.println("参数校验通过");
}
@GetMapping("/bean-check/test2")
public void beanCheckTest2(@Valid UserDTO userDTO, BindingResult bindingResult) {
System.out.println("参数校验通过");
}
}
注意:
①Controller类上的@Validated注解和controller方法中的@Valid注解都不能少,否则校验不会生效。
②这里有两个controller方法,分别展示了Post方法接收DTO实体类参数的参数校验和Get方法接收DTO实体类参数的参数校验的情形。
③这里方法中的BindingResult,存储了校验结果,可以用于后续统一异常处理
使用postman进行测试:
post:
后台输出:
javax.validation.ConstraintViolationException:
beanCheckTest1.userDTO.liveInEarth: liveInEarth必须为true,
beanCheckTest1.userDTO.name: name不能为空字符串,
beanCheckTest1.userDTO.age: age不能超过100,
beanCheckTest1.userDTO.password: password所含字符数在[2,6]的范围内
get:
后台输出:
javax.validation.ConstraintViolationException:
beanCheckTest2.userDTO.password: password所含字符数在[2,6]的范围内,
beanCheckTest2.userDTO.name: name不能为空字符串,
beanCheckTest2.userDTO.age: age不能超过100,
beanCheckTest2.userDTO.liveInEarth: liveInEarth必须为true
3. 校验模式
在上面实体类参数校验的例子中,可以看到会校验所有字段,并将所有不符合校验规则的字段打印出来。这种校验策略是实体类参数校验的普通校验模式(默认的校验模式)。实际上有两种校验模式:
①普通模式(默认是这个模式): 会校验完所有的属性,然后返回所有的验证失败信息
②快速失败模式: 只要有一个验证失败,则返回。
如果想要配置第二种模式,需要添加如下配置类:
新建配置类ValidateStrategyConf:
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ValidateStrategyConf {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
再次发送请求:
Get:
后台打印:
可以看到校验到name字段不符合校验规则后,即停止校验了。