Spring Boot项目传参校验的最佳实践指南


Posted in Java/Android onApril 05, 2022

场景还原

简单业务场景模拟:

假如你现在在做一个成绩录入系统,你愉快地用Spring Boot框架写了一个后台接口,用于接收前台浏览器传过来的 Student对象,并插入后台数据库。

我们将传入的 Student对象定义为:

public class Student {
    private String name;    // 姓名
    private Integer score;  // 考试分数(满分100分)
    private String mobile;  // 电话号码(11位)
}

然后写一个Post请求的后台接口,来接收网页端传过来的 Student对象:

@RestController
public class TestController {

    @Autowired
    private StudentService studentService;

    @PostMapping("/add")
    public String addStudent( @RequestBody Student student ) {
        studentService.addStudent( student ); // 将student对象存入数据库
        return "SUCCESS";
    }
}

此时我想你一定看出来了上面这段代码的漏洞,因为我们并没有对传入的 Student对象做任何数据校验,比如:

Student对象里三个字段的某一个忘传了,为 null怎么办?Student的 score分数,假如写错了,写成 101分怎么办?Student的 mobile11位手机号码,假如填错了,多写了一位怎么办?...等等

这些数据虽然在前端页面一般会做校验,但我们作为一个严谨且良心的后端开发工程师,我们肯定要对传入的每一项数据做严格的校验,所以我们应该怎么写?

@PostMapping("/add")
public String addStudent( @RequestBody Student student ) {
    if( student == null )
        return "传入的Student对象为null,请传值";
    if( student.getName()==null || "".equals(student.getName()) )
        return "传入的学生姓名为空,请传值";
    if( student.getScore()==null )
        return "传入的学生成绩为null,请传值";
    if( (student.getScore()<0) || (student.getScore()>100) )
        return "传入的学生成绩有误,分数应该在0~100之间";
    if( student.getMobile()==null || "".equals(student.getMobile()) )
        return "传入的学生电话号码为空,请传值";
    if( student.getMobile().length()!=11 )
        return "传入的学生电话号码长度有误,应为11位";
    studentService.addStudent( student ); // 将student对象存入MySQL数据库
    return "SUCCESS";
}

写是写完了,就是感觉手有点酸,并且心有点累,这个 Student对象倒还好,毕竟内部仅3个字段,假如一个复杂的对象有30个字段怎么办?简直不敢想象!

神注解加持

其实Spring框架很早版本开始,就通过注解的方式,来方便地为我们提供了各项交互数据的校验工作,比如上面的例子,我们只需要在传入的 Student实体类的字段中加入对应注解即可方便的解决问题:

public class Student {
    @NotNull(message = "传入的姓名为null,请传值")
    @NotEmpty(message = "传入的姓名为空字符串,请传值")
    private String name;    // 姓名

    @NotNull(message = "传入的分数为null,请传值")
    @Min(value = 0,message = "传入的学生成绩有误,分数应该在0~100之间")
    @Max(value = 100,message = "传入的学生成绩有误,分数应该在0~100之间")
    private Integer score;  // 分数

    @NotNull(message = "传入的电话为null,请传值")
    @NotEmpty(message = "传入的电话为空字符串,请传值")
    @Length(min = 11, max = 11, message = "传入的电话号码长度有误,必须为11位")
    private String mobile;  // 电话号码
}

当然,于此同时,我们还需要在对象入口处,加上注解 @Valid来开启对传入 Student对象的验证工作:

@PostMapping("/add")
public String addStudent( @RequestBody  @Valid Student student ) {
    // 棒棒哒!原先各种繁杂的参数校验工作统统都省了!一行代码不用写
    studentService.addStudent( student ); // 将student对象存入MySQL数据库
    return "SUCCESS";
}

这时候,如果某个字段传入错误,比如我传数据的时候,将学生的成绩误传为 101分,则接口返回结果便会提示出错误详情:

Spring Boot项目传参校验的最佳实践指南

 

当然,关于这个事情的原理,既然用到了注解,无非用的也就是Java里的各种反射等知识来实现的,感兴趣的小伙伴可以借此机会研究一下!

数据异常统一拦截

上面利用注解的方式做统一数据校验感觉十分美好,但唯一美中不足的就是返回的结果太过繁杂,不一定使我们需要的格式,我们需要做统一处理,比如:我只想将具体参数校验的错误提示信息给抠出来返回给前端即可。

为此,我们为项目配置全局统一异常拦截器来格式化所有数据校验的返回结果。

@ControllerAdvice
@ResponseBody
public class GlobalExceptionInterceptor {
  @ExceptionHandler(value = Exception.class)
  public String exceptionHandler(HttpServletRequest request, Exception e) {
    String failMsg = null;
    if (e instanceof MethodArgumentNotValidException) {
      // 拿到参数校验具体异常信息提示
      failMsg = ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage();
    }
    return failMsg; // 直接吐回给前端
  }
}

如上面代码所示,我们全局统一拦截了参数校验异常 MethodArgumentNotValidException,并仅仅只拿到对应异常的详细 Message信息吐给前端,此时返回给前端的数据就清楚得多:

Spring Boot项目传参校验的最佳实践指南

可以的,非常优雅!

总结

到此这篇关于Spring Boot项目传参校验的文章就介绍到这了,更多相关SpringBoot项目传参校验内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
ConstraintValidator类如何实现自定义注解校验前端传参
Jun 18 Java/Android
新手入门Jvm-- JVM对象创建与内存分配机制
Jun 18 Java/Android
详解Java实践之适配器模式
Jun 18 Java/Android
Java Optional<Foo>转换成List<Bar>的实例方法
Jun 20 Java/Android
解决ObjectMapper.convertValue() 遇到的一些问题
Jun 30 Java/Android
浅谈resultMap的用法及关联结果集映射
Jun 30 Java/Android
java设计模式--七大原则详解
Jul 21 Java/Android
SpringDataJPA实体类关系映射配置方式
Dec 06 Java/Android
SpringBoot中HttpSessionListener的简单使用方式
Mar 17 Java/Android
剑指Offer之Java算法习题精讲二叉树的构造和遍历
Mar 21 Java/Android
Java 数据结构七大排序使用分析
Apr 02 Java/Android
openGauss数据库JDBC环境连接配置的详细过程(Eclipse)
Jun 01 Java/Android
springboot入门 之profile设置方式
Apr 04 #Java/Android
Java实现经典游戏泡泡堂的示例代码
Dubbo+zookeeper搭配分布式服务的过程详解
SpringBoot整合minio快速入门教程(代码示例)
Apr 03 #Java/Android
SpringBoot整合Minio文件存储
Apr 03 #Java/Android
Java中Quartz高可用定时任务快速入门
Apr 03 #Java/Android
Spring Security使用单点登录的权限功能
You might like
php中文验证码实现示例分享
2014/01/12 PHP
PHP实现的简单mock json脚本分享
2015/02/10 PHP
PHP关键特性之命名空间实例详解
2017/05/06 PHP
javascript TextArea动态显示剩余字符
2008/10/22 Javascript
javascript先序遍历DOM树的方法
2016/02/27 Javascript
原生js实现吸顶效果
2017/03/13 Javascript
Angular.js通过自定义指令directive实现滑块滑动效果
2017/10/13 Javascript
vue仿淘宝订单状态的tab切换效果
2020/06/23 Javascript
基于VUE移动音乐WEBAPP跨域请求失败的解决方法
2018/01/16 Javascript
使用Vue做一个简单的todo应用的三种方式的示例代码
2018/10/20 Javascript
详解element-ui设置下拉选择切换必填和非必填
2019/06/17 Javascript
vue实现codemirror代码编辑器中的SQL代码格式化功能
2019/08/27 Javascript
vue动态路由:路由参数改变,视图不更新问题的解决
2019/11/05 Javascript
微信小程序学习总结(四)事件与冒泡实例分析
2020/06/04 Javascript
JavaScript Event Loop相关原理解析
2020/06/10 Javascript
js实现简单的无缝轮播效果
2020/09/05 Javascript
基于vuex实现购物车功能
2021/01/10 Vue.js
浅谈Python peewee 使用经验
2017/10/20 Python
python 读取dicom文件,生成info.txt和raw文件的方法
2019/01/24 Python
python中的print()输出
2019/04/12 Python
Python numpy数组转置与轴变换
2019/11/15 Python
详解python os.path.exists判断文件或文件夹是否存在
2020/11/16 Python
HTML5图片层叠的实现示例
2020/07/07 HTML / CSS
英国领先的运动营养品牌:Protein Dynamix
2018/01/02 全球购物
高一家长会邀请函
2014/01/12 职场文书
幼儿园国庆节活动方案
2014/02/01 职场文书
模特大赛策划方案
2014/05/28 职场文书
普通党员对照检查材料
2014/08/28 职场文书
乡镇干部个人对照检查材料思想汇报(原创篇)
2014/09/28 职场文书
财务审计整改报告
2014/11/06 职场文书
五四青年节比赛演讲稿
2015/03/18 职场文书
2017大学生寒假社会实践心得体会
2016/01/14 职场文书
英语教学课后反思
2016/02/15 职场文书
优秀共产党员事迹材料2016
2016/02/29 职场文书
Go中的条件语句Switch示例详解
2021/08/23 Golang
MySQL串行化隔离级别(间隙锁实现)
2022/06/16 MySQL