注解式参数校验@Valid,拒绝if-else炼狱

前言

在软件开发中,与客户进行数据的交互是比较频繁的数据采集方式,而为了确保数据的正确落库,数据校验的部分必不可少。参数校验具体指的是对输入数据的大小、类型、正则匹配等对数据本身属性的校验,这一块的校验通常是在前端进行第一轮校验,确保参数具备一定逻辑正确性才将其打包至后端处理,而为了进一步增强安全性,也同样需要在后端板块进行二次校验。而对于此类校验,最常见也是最臃肿的处理方案就是if-else炼狱,如果说参数个数较少,那么无可厚非,但如果包含了七八个参数,那么这就很拉跨了,下面介绍一种基于注解的校验方案,进一步优化校验参数代码的可读性。


效果演示

本例子基于通过在实体类映射接收请求参数的MVC接口,对实体类中需要对属性进行如下验证:

  • name - 用户姓名,不能为空;
  • age - 年龄,大于0小于20;
  • phone - 手机号,满足手机号格式;

实体类代码

less 复制代码
 @Data
 public class TestPojo {
   @NotBlank(message = "不满足时抛出的自定义异常回馈信息")
   private String name;
   
   @Min(value = 0, message = "不满足时抛出的自定义异常回馈信息")
   @Max(value = 20, message = "不满足时抛出的自定义异常回馈信息")
   private Integer age;
   
   @Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\d{8}$", message = "不满足时抛出的自定义异常回馈信息")
   private String phone;
 }

业务接口

less 复制代码
@PostMapping("/test")
public ResultVO test(@Valid @RequestBody TestPojo testPojo) {
     // 直接写业务代码,无需编写if-else校验....
    
     return ResultVO.ok("响应数据");
 }

结果VO类

kotlin 复制代码
@Data
public class ResultVO<T> implements Serializable {
    private int code;
    private boolean success;
    private T data;
    private String msg;
​
    private ResultVO(int code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
        this.success = code == 200;
    }
​
    public static <T> ResultVO<T> ok(T data) {
        return new ResultVO<>(200, data, null);
    }
​
    public static <T> ResultVO<T> error(String msg) {
        return new ResultVO<>(500, null, msg);
    }
}

全局异常处理类

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    /*  
     *  这里的BindException不是自定义的,是该参数校验框架自带的
     *  当设置的参数规则不满足,就会抛出该类型的异常,并且把不满足的哪个校验参数自定义的异常信息封装到该对象中
     */
    @ExceptionHandler(BindException.class)  
    public ResultVO handleError(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        return R.error(bindingResult.getFieldError().getDefaultMessage());
    }
}

通过以上代码,就可以实现对接收的请求数据的参数校验,并且当校验失败就会抛出指定异常被异常处理类进行捕捉处理,返回自定义的不满足参数所对应的自定义提示信息


快速使用

引入依赖

xml 复制代码
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-validation</artifactId>
 </dependency>

编写校验实体类

其实这一块和上方"结果VO类"的演示中可以了解到使用的方式,咱主要要知道它有哪些常用的校验注解可以使用就好

常用注解 作用解释
@NotNull 值不能为null
@NotEmpty 字符串、集合或数组的值不能为空,即长度大于0
@NotBlank 字符串的值不能为空白,即不能只包含空格
@Size 字符串、集合或数组的大小是否在指定范围内
@Min 数值的最小值
@Max 数值的最大值
@DecimalMin 数值的最小值,可以包含小数
@DecimalMax 数值的最大值,可以包含小数
@Pattern 字符串是否匹配指定的正则表达式

还有许多有关的校验可以自行查询,这里不作过多赘述

校验失败异常捕获处理

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    /*  
     *  这里的BindException不是自定义的,是该参数校验框架自带的
     *  当设置的参数规则不满足,就会抛出该类型的异常,并且把不满足的哪个校验参数自定义的异常信息封装到该对象中
     */
    @ExceptionHandler(BindException.class)  
    public ResultVO handleError(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        String 自定义的不满足参数校验结果信息 = bindingResult.getFieldError().getDefaultMessage();
    }
}

启用校验

其实就是在接收实体类上加一个@Valid注解就可以启用实体类中校验逻辑

less 复制代码
@PostMapping("/test")
public void test(@Valid @RequestBody TestPojo testPojo) {
     // 直接写业务代码,无需编写if-else校验....
 }

也许会有疑惑,一个实体类我不一定要用来校验数据呀,这样子设置后会不会以后用这个实体类都要进行参数校验了?并不会,不想校验就不要加@Valid注解就完事了嘛


总结

通过以上的方案,我们可以较为简洁实现简单的参数校验,为shi山的削减贡献了一份力量,当然,我们会发现通过这种方案,我们能够校验的方式实际上是比较单薄的,往往我们需要针对项目业务需求对参数进行一些自定义的校验逻辑,那么这点可以参考我写的另一篇文章:基于 AOP + 自定义注解 的校验业务讲解,希望本文对大家有所帮助。

相关推荐
IT小白杨1 分钟前
从环境制备到自动化工作流:多账号运营的工程化架构拆解
java·经验分享·自动化·安全架构·指纹浏览器
豆瓣鸡26 分钟前
算法日记 - Day3
java·开发语言·算法
萧瑟余晖42 分钟前
Java深入解析篇九之NIO详解
java·网络·nio
mldong1 小时前
从 mldong 到 jeeflow:一个工作流引擎的独立进化
后端
The Chosen One9851 小时前
高进度算法模板速记(待完善)
java·前端·算法
陈随易1 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
Aaron - Wistron2 小时前
Web API C# (Furion版)带 单元测试
开发语言·后端·c#
卷无止境2 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境2 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
极光代码工作室3 小时前
基于SpringBoot的课程预约系统
java·springboot·web开发·后端开发