网站首页 > 技术教程 正文
作者 | 阿呆很呆非常呆
来源 | urlify.cn/Jraqea
一、简介
后台业务入口类Controller,对于入参的合法性校验,可以简单粗暴的写出一堆的 if 判断,如下:
@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("saveUser")
public String saveUser(UserInfoVo userInfoVo){
if(StrUtil.isBlank(userInfoVo.getUserName())){
return "userName is not null";
}
if(StrUtil.isBlank(userInfoVo.getPwd())){
return "pwd is not null";
}
return "save success";
}
}
二、重要说明
2.1、springboot在2.3之后,spring-boot-starter-web的依赖项已经去除了validate依赖,推荐导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.2、关于 @Valid 和 @Validated
@Validated 是Spring Validation验证框架对JSR-303规范的一个扩展, javax提供@Valid 是标准的JSR-303规范。使用基本无区别,但是在Group分组使用上还是使用 @Validated方便。
嵌套验证上,必须在待验证的vo中的嵌套实体属性上增加@Valid。
三、实验出真知
3.1、牛刀小试
定义VO,在需要校验的字段上加上相应注解
@Data
public class UserInfoVo {
@NotBlank(message = "userName is not null")
private String userName;
@NotNull(message = "age is not null")
private Integer age;
@NotBlank(message = "pwd is not null")
private String pwd;
}
Controller入参加上@Valid、校验结果BindingResult:
@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("saveUser")
public String saveUser(@Valid UserInfoVo userInfoVo, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return bindingResult.getAllErrors().get(0).getDefaultMessage();
}
return "save success";
}
}
通过工具访问,可看到如果入参不符合,会有相应message返回
以上就完成了一个最简单的优雅校验过程,其中内置常用校验类型:
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) Validates that the annotated string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=) Checks whether the annotated value lies between (inclusive) the specified minimum and maximum.
@Range(min=10000,max=50000,message="range.bean.wage")
private BigDecimal wage;
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)
3.2、第一次改版
入参很多的情况下,可能会同时产生多个不同的错误校验,那么如果每次只是返回一个错误提示,每次客户端改一个,那么体验是极差的。基于此,封装返回类,提供统一返回。
@Data
@NoArgsConstructor
@RequiredArgsConstructor
public class ResponseData<T> {
@NonNull
private Integer code;
@NonNull
private String message;
private T data;
public static ResponseData success() {
return new ResponseData(HttpStatus.OK.value(), "SUCCESS");
}
public static ResponseData success(Object data) {
ResponseData entity = success();
entity.setData(data);
return entity;
}
public static ResponseData fail(Integer code, String msg) {
return new ResponseData(code, msg);
}
public static ResponseData fail(Integer code, String msg, Object data) {
ResponseData entity = fail(code, msg);
entity.setData(data);
return entity;
}
}
@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("saveUser")
public ResponseData saveUser(@Valid UserInfoVo userInfoVo, BindingResult bindingResult){
if(bindingResult.hasErrors()){
List<String> collect = bindingResult.getFieldErrors().stream().map(item -> item.getDefaultMessage()).collect(Collectors.toList());
return ResponseData.fail(900, "req param invalid", collect);
}
return ResponseData.success();
}
}
3.3、第二次改版
上述改版,已经能够一次性的返回所有未校验通过异常,但是,每个方法中都这么来一遍,还是挺麻烦的。下面利用统一异常处理参数校验,改造完成后Controller中专注于业务处理即可
@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("saveUser")
public ResponseData saveUser(@Valid UserInfoVo userInfoVo){
return ResponseData.success();
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Integer BAD_REQUEST_CODE = 900;
private static final String BAD_REQUEST_MSG = "req param invalid";
@ExceptionHandler(BindException.class)
public ResponseData bindExceptionHandler(BindException exception){
List<String> collect = exception.getAllErrors().stream().map(item -> item.getDefaultMessage())
.collect(Collectors.toList());
return ResponseData.fail(BAD_REQUEST_CODE, BAD_REQUEST_MSG, collect);
}
@ExceptionHandler(Exception.class)
public ResponseData exceptionHandler(Exception exception){
return ResponseData.fail(500, exception.getMessage());
}
}
3.4、分组校验
通常,存在场景:name参数在注册接口必须非空,但是修改接口无所谓。那么此时,分组group很好解决问题
/**
* @author cfang 2020/9/23 10:47
*
* 关于 extends Default
* 继承 Default 的话,所有定义校验规则的都会校验
* 不继承的话,则只校验加了group信息的校验字段
*/
public interface UserGroup extends Default{
}
@Data
public class UserInfoVo {
@NotBlank(message = "userName is not null", groups = UserGroup.class)
private String userName;
@NotNull(message = "age is not null")
private Integer age;
@NotBlank(message = "pwd is not null")
private String pwd;
}
@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("saveUser")
public ResponseData saveUser(@Validated({UserGroup.class}) UserInfoVo userInfoVo){
return ResponseData.success();
}
@PostMapping("updateUser")
public ResponseData updateUser(@Valid UserInfoVo userInfoVo){
return ResponseData.success();
}
@InitBinder
public void init(HttpServletRequest request, DataBinder dataBinder){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false); //是否校验转化日期格式,true-转化日期,false-参数错误则直接异常。eg. 2020-55-10, true->2024-10-10 , false-异常报错。默认值true
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
}
3.5、递归校验
@Data
public class AddressInfoVo {
@NotBlank(message = "street is not null")
private String street;
}
@Data
public class UserInfoVo {
@NotBlank(message = "userName is not null", groups = UserGroup.class)
private String userName;
@NotNull(message = "age is not null")
private Integer age;
@NotBlank(message = "pwd is not null")
private String pwd;
@Past(message = "predate is invalid")
@NotNull(message = "predate is not null")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
private Date preDate;
@Valid
private AddressInfoVo infoVo;
}
@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("saveUser")
public ResponseData saveUser(@Validated({UserGroup.class}) @RequestBody UserInfoVo userInfoVo){
return ResponseData.success();
}
@PostMapping("updateUser")
public ResponseData updateUser(@Valid @RequestBody UserInfoVo userInfoVo){
return ResponseData.success();
}
@InitBinder
public void init(HttpServletRequest request, DataBinder dataBinder){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Integer BAD_REQUEST_CODE = 900;
private static final String BAD_REQUEST_MSG = "req param invalid";
@ExceptionHandler(BindException.class)
public ResponseData bindExceptionHandler(BindException exception){
List<String> collect = exception.getAllErrors().stream().map(item -> item.getDefaultMessage())
.collect(Collectors.toList());
return ResponseData.fail(BAD_REQUEST_CODE, BAD_REQUEST_MSG, collect);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseData argInvalidExceptionHandler(MethodArgumentNotValidException exception){
List<String> collect = exception.getBindingResult().getFieldErrors().stream().map(item -> item.getDefaultMessage())
.collect(Collectors.toList());
return ResponseData.fail(BAD_REQUEST_CODE, BAD_REQUEST_MSG, collect);
}
@ExceptionHandler(Exception.class)
public ResponseData exceptionHandler(Exception exception){
return ResponseData.fail(500, exception.getMessage());
}
}
猜你喜欢
- 2024-09-24 SAP执行QS51维护UD的选择集,报错 – Transaction no longer valid
- 2024-09-24 开机显示invalid partition table(无效的磁盘分区)
- 2024-09-24 2021-09-22:请你判断一个 9x9 的数独是否有效。只需要 根据以下规
- 2024-09-24 编码规范-Python:valid-classmethod-first-arg限制
- 2024-09-24 最好用的MODBUS测试软件MODSCAN
- 2024-09-24 Ubuntu系统怎么设置静态ip?同样适用于深度系统的教程
- 2024-09-24 tomcat8拦截不合法字符的解决方法
- 2024-09-24 LeetCode 65. Valid Number
- 2024-09-24 SpringMVC,SpringBoot快速验证参数是否完整 基于@Valid注解
- 2024-09-24 springboot中注解校验@Valid@Validated失效场景汇总(持续更新)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- sd分区 (65)
- raid5数据恢复 (81)
- 地址转换 (73)
- 手机存储卡根目录 (55)
- tcp端口 (74)
- project server (59)
- 双击ctrl (55)
- 鼠标 单击变双击 (67)
- debugview (59)
- 字符动画 (65)
- flushdns (57)
- ps复制快捷键 (57)
- 清除系统垃圾代码 (58)
- web服务器的架设 (67)
- 16进制转换 (69)
- xclient (55)
- ps源文件 (67)
- filezilla server (59)
- 句柄无效 (56)
- word页眉页脚设置 (59)
- ansys实例 (56)
- 6 1 3固件 (59)
- sqlserver2000挂起 (59)
- vm虚拟主机 (55)
- config (61)
本文暂时没有评论,来添加一个吧(●'◡'●)