Java参数校验
JSR 380 ¶
JSR 380规范是bean验证的Java api规范,JavaEE和JavaSE的一部分,使用注解如@NotNull, @Min, and @Max,确保bean属性符合一定条件
jsr380需要Java8或以上版本,利用Java8中新增的特性,如注解类型,支持新的类如:Optional 和 LocalDate
Hibernate Validator是验证规范的参考实现,使用时需要导入该依赖
@Validation与@Valid区别 ¶
来源
@Validated:是Spring 做得一个自定义注解,做了增强功能@Valid:是JSR-303规范标准注解支持,是一个标记注解
注解位置
@Validated:用在类型、方法和方法参数上。但不能用于成员属性(field)@Valid:可以用在方法、构造函数、方法参数和成员属性(field)上
分组校验
@Validated:提供分组功能,可以在参数验证时,根据不同的分组采用不同的验证机制@Valid:没有分组功能
嵌套校验
@Validated:不支持嵌套校验@Valid:支持
总结
| 区别 | @Valid | @Validated |
|---|---|---|
| 提供者 | JSR-303规范 | Spring |
| 是否支持分组 | 不支持 | 支持 |
| 标注位置 | METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE | TYPE, METHOD, PARAMETER |
| 嵌套校验 | 支持 | 不支持 |
快速失败 ¶
默认会校验完所有字段,然后才抛出异常。可以通过一些简单的配置,开启Fali Fast模式,一旦校验失败就立即返回
1@Bean
2public Validator validator() {
3 return Validation.byProvider(HibernateValidator.class)
4 .configure()
5 .failFast(true) //关闭快速失败
6 .buildValidatorFactory().getValidator();
7}统一异常处理 ¶
1@RestControllerAdvice
2public class CommonExceptionHandler {
3
4 @ExceptionHandler({MethodArgumentNotValidException.class})
5 @ResponseStatus(HttpStatus.OK)
6 @ResponseBody
7 public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
8 BindingResult bindingResult = ex.getBindingResult();
9 StringBuilder sb = new StringBuilder("校验失败:");
10 for (FieldError fieldError : bindingResult.getFieldErrors()) {
11 sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
12 }
13 String msg = sb.toString();
14 return msg;
15 }
16
17 @ExceptionHandler({ConstraintViolationException.class})
18 @ResponseStatus(HttpStatus.OK)
19 @ResponseBody
20 public Object handleConstraintViolationException(ConstraintViolationException ex) {
21 return ex.getMessage();
22 }
23}使用案例 ¶
导入依赖
1<!--数据校验api接口-->
2<dependency>
3 <groupId>Javax.validation</groupId>
4 <artifactId>validation-api</artifactId>
5</dependency>
6<!--数据校验api实现 hibernate-validator只不过是一堆数据校验接口的实现类,和数据库没有半点关系-->
7<dependency>
8 <groupId>org.hibernate.validator</groupId>
9 <artifactId>hibernate-validator</artifactId>
10</dependency>基础校验 ¶
实体类
1@Data
2public class User {
3 @NotBlank(message = "不能为空或空串")
4 private String name;
5
6 @Max(value = 60,message = "年龄不能大于60")
7 @NotNull(message = "不能为null")
8 private Integer age;
9}controller
1@RequestMapping("/validatedTest_One")
2public void validatedTest_One(@Validated User user) {
3 System.out.println("---------");
4}此时使用 @Validated 或者 @Valid 注解都可
分组校验 ¶
实体类
1@Data
2public class User {
3 @NotBlank(message = "不能为空或空串",groups = GroupOne.class)
4 private String name;
5
6 @Min(value = 10,message = "年龄不能小于10")
7 @NotNull(message = "不能为null")
8 private Integer age;
9
10 public interface GroupOne {
11
12 }
13
14 public interface GroupTwo{
15
16 }
17}controller
1@RequestMapping("/validatedTest_One")
2public void validatedTest_One(@Validated({User.GroupOne.class}) User user) {
3 System.out.println("---------");
4}@Valid不支持分组校验,@Validated支持分组校验;未指定分组接口的字段,默认为Default分组;分组接口可以实现Default接口,这时使用@Validated指定实现了Default分组的接口时,会校验Default分组的字段
嵌套校验 ¶
实体类
1@Data
2public class User {
3 @NotBlank(message = "不能为空或空串",groups = GroupOne.class)
4 private String name;
5
6 @Min(value = 10,message = "年龄不能小于10")
7 @NotNull(message = "不能为null")
8 private Integer age;
9
10 @Valid
11 @NotNull()
12 private A a;
13
14 @Data
15 public class A{
16 @NotBlank(message = "不能为空")
17 private String field1;
18 private String field2;
19 }
20
21 public interface GroupOne {}
22
23 public interface GroupTwo{}
24}controller
1@RequestMapping("/validatedTest_Two")
2public void validatedTest_Two(@RequestBody @Valid User user) {
3 System.out.println("---------");
4}嵌套校验,需要校验的实体类字段必须使用@Valid注解,controller层可以使用@Valid或者 @Valid;当实体类的字段的对象为null时,默认会校验通过
集合校验 ¶
如果请求体直接传递了json数组给后台,并希望对数组中的每一项都进行参数校验。此时,如果直接使用Java.util.Collection下的list或者set来接收数据,参数校验并不会生效!可以使用自定义list集合来接收参数
编程式校验 ¶
除了通过controller接收参数时,使用注解校验,还可以通过编程式手动触发校验
需要注入一个Javax.validation.Validator的实现
1@Resource
2private Validator validator;
3
4@RequestMapping("/validatedTest_Three")
5public void validatedTest_Three(@RequestBody User user) {
6 //validate 为校验结果,如果无校验失败信息为空
7 Set<ConstraintViolation<User>> validate = validator.validate(user);
8}检验普通参数 ¶
在类名上是使用@Validated注解,此时方法入参可以使用注解校验简单入参
1@RestController
2@RequestMapping("/test")
3@Validated
4public class TestController {
5 @RequestMapping("/validatedTest_Four")
6 public void validatedTest_Four(@NotBlank String id) {
7 System.out.println(id);
8 }
9}普通项目中使用验证 ¶
实体类
1@Data
2@Accessors(chain = true)
3public class UserDto {
4
5 @NotBlank(message = "必填项")
6 private String id;
7
8 private String name;
9
10 @NotNull
11 @Max(value = 20,message = "最大为20岁")
12 private Integer age;
13}Demo
1public class Demo {
2 public static Validator validator= null;
3
4 static {
5 validator= Validation.byProvider(HibernateValidator.class)
6 .configure()
7 .failFast(false) //关闭快速失败
8 .messageInterpolator(new ParameterMessageInterpolator())
9 .buildValidatorFactory().getValidator();
10 }
11
12 public static void main(String[] args) {
13 UserDto userDto=new UserDto();
14 userDto.setId(null)
15 .setName("lei")
16 .setAge(22);
17 Set<ConstraintViolation<UserDto>> violations = validator.validate(userDto);
18 if (!violations.isEmpty()) {
19 StringBuilder stringBuilder=new StringBuilder();
20 violations.forEach(e->stringBuilder.append(e.getPropertyPath().toString()+e.getMessage()));
21 throw new RuntimeException(stringBuilder.toString());
22 }
23 }
24}