文章目录
- 前言
- Bean Validation
- 注解
- 实践出真知
- 异常处理
- 总结
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
前言
工作中我们经常会遇到验证字段是否必填,或者字段的值是否在给定范围之内等等类似的问题,如果说是一两个字段的验证还好,验证的字段很多的话,代码就会被大量的if语句包围。通常来说,这些关于字段的判断应该和业务逻辑分开来,可能我们想到的第一个解决方案就是通过AOP,这也能解决我们的问题的。但实际上大可不必,作为一个成熟的语言,Java已经给我们提供解决方案了,那就是Bean Validation。
Bean Validation
JSR-303是Java EE 6中的一项子规范,名为Bean Validation,这是Bean Validation 1.0 版本,目前已发展到到3.0版本,名为Jakarta Bean Validation 3.0。Bean Validation提供了一个数据验证的框架,用于对Java Bean中的字段的值进行验证。它使得基本的验证逻辑可以从业务代码中脱离出来,成为一个独立的验证层。
JSR-303的官方参考实现是Hibernate Validator。Hibernate Validator提供了JSR 303规范中所有内置约束的实现,除此之外还有一些附加的约束。
这种验证机制是运行时的,也就是说,在验证之后,如果数据不符合指定的约束,那么会立即返回错误信息。
总之,JSR-303 为Java应用程序提供了一种方便、灵活且强大的数据验证方式。
注解
JSR-303 提供了一系列注解,用于在Java中进行数据校验。这些注解主要用于对实体类的属性进行约束,以确保数据的有效性。
以下是一些常用的JSR-303 validation注解:
- @NotNull:用于对象的校验,确保对象不为null。
- @NotBlank:验证对象是否不为空,相比
@NotNull
会去掉首尾空格,对象类型为CharSequence。 - @NotEmpty: 验证对象(如数组、Collection集合、Map、String)是否不为NULL并且长度或者大小不为空 。
- @Size:用于验证对象(如数组、Collection集合、Map、String)的长度或大小是否在给定的范围之内。
- @Pattern:验证字符串是否匹配指定的正则表达式,null值被认为是有效的格式。
- @Email:验证是否符合电子邮件格式。
- @Min:验证数字是否大于等于指定值,
- @Max:验证数字是否小于等于指定值。
- @AssertTrue:验证Boolean对象是否为true。
- @AssertFalse:验证Boolean对象是否为false。
- @NotBlank:验证CharSequence 对象非null,且长度必须大于0。
- @DecimalMin(value):被注解的对象必须是一个数字,其值必须大于等于指定的最小值,对象类型可以为 BigDecimal、BigInteger、CharSequence。
- @DecimalMax:被注解的对象必须是一个数字,其值必须小于等于指定的最大值。
- @Digits(integer,fraction):被注解的元素必须是一个数字,其值必须在指定的整数和小数部分的最大位数的范围之内。
- @Past:被注解的元素必须是一个过去的日期。
- @Future:被注解的元素必须是一个将来的日期。
- @FutureOrPresent:被注解的元素必须是现在或将来的一个瞬间、日期或时间。
- @PositiveOrZero:被注解的元素必须为正数或零。
- @Positive:被注解的元素必须是正数(不包括0)。
- @NegativeOrZero:被注解的元素必须为负数或零。
- @Negative:被注解的元素必须是负数(不包括0)。
- @Null:被注解的元素必须是NULL。
Hibernate Validator 附加的约束注解:
Hibernate Validator 8.0.1官方链接 感兴趣的可以去看看。
实践出真知
下面通过代码演示一下Springboot 中字段验证的使用。
1. 引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2. 创建Bean 用于校验
@Data
public class UserBean {@NotEmptyprivate String username;@Min(value = 18)private Integer age;@Email(message = "邮箱格式不正确")private String email;
}
3. 创建访问接口
接口中要使用 @Validated
或者 @Valid
使Bean 验证生效,下一篇文章 @Validated
和 @Valid
之间的区别。
@RestController
@RequestMapping("validation")
public class ValidationController {@GetMapping("user")public UserBean getUserBean(@Validated UserBean userBean) {return userBean;}
}
4. 使用postman进行测试
因为username
校验不通过,所以postman响应结果如下,响应结果不够友好,下面会进行改造:
{"timestamp": "2024-02-28T09:15:23.925+00:00","status": 400,"error": "Bad Request","path": "/validation/user"
}
控制台打印结果比较详细:
2024-02-28T17:15:23.918+08:00 WARN 24752 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.web.bind.MethodArgumentNotValidException:Validation failed for argument [0] in public site.suncodernote.validation.UserBean site.suncodernote.validation.ValidationController.getUserBean(site.suncodernote.validation.UserBean): [Field error in object 'userBean' on field 'username': rejected value [null]; codes [NotEmpty.userBean.username,NotEmpty.username,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userBean.username,username]; arguments []; default message [username]]; default message [不能为空]] ]
异常处理
由于默认的校验提示不够友好,无法具体显示是哪个字段出现的问题,下面我们将其简单改造一下。
1. 封装统一响应对象
@Data
public class ResponseResult <T> implements Serializable {private boolean success;/*** 返回处理消息*/private String message;/*** 返回代码*/private Integer code = 0;/*** 返回数据对象 data*/private T result;
}
2. 封装全局异常处理类
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 参数校检异常* @param e* @return*/@ExceptionHandler(value = MethodArgumentNotValidException.class)public ResponseResult<?> handle(MethodArgumentNotValidException e) {BindingResult bindingResult = e.getBindingResult();StringJoiner joiner = new StringJoiner(";");for (ObjectError error : bindingResult.getAllErrors()) {// 注解名称String code = error.getCode();String[] codes = error.getCodes();String property = codes[1];property = property.replace(code ,"").replace(".","");String defaultMessage = error.getDefaultMessage();joiner.add(property+defaultMessage);}return handleException(joiner.toString());}private ResponseResult<?> handleException(String msg) {ResponseResult<?> result = new ResponseResult<>();result.setMessage(msg);result.setCode(500);return result;}
}
在 GlobalExceptionHandler
异常处理类中,为了省事就把字段名和验证失败提示语拼接到一起了。
3. 再次测试
再次测试可以看到在响应结果中得到了我们想要的结果了,至此Springboot参数校验入门就完成了。
总结
Springboot 参数校验在实际工作中用处非常大,本文只是简单介绍一下有哪些注解和简单使用,后续会对Springboot参数校验做一个详细的介绍和使用,感兴趣可以关注一下。