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 相关文章推荐
Java并发编程之Executor接口的使用
Jun 21 Java/Android
详解Java实现设计模式之责任链模式
Jun 23 Java/Android
SpringBoot实现异步事件驱动的方法
Jun 28 Java/Android
Spring mvc是如何实现与数据库的前后端的连接操作的?
Jun 30 Java/Android
使用@Value值注入及配置文件组件扫描
Jul 09 Java/Android
Spring this调用当前类方法无法拦截的示例代码
Mar 20 Java/Android
剑指Offer之Java算法习题精讲二叉树专项训练
Mar 21 Java/Android
Netty客户端接入流程NioSocketChannel创建解析
Mar 25 Java/Android
Java 超详细讲解hashCode方法
Apr 07 Java/Android
SpringCloud项目如何解决log4j2漏洞
Apr 10 Java/Android
Java工作中实用的代码优化技巧分享
Apr 21 Java/Android
带你了解Java中的ForkJoin
Apr 28 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中取得image按钮传递的name值
2006/10/09 PHP
CodeIgniter配置之autoload.php自动加载用法分析
2016/01/20 PHP
php中的抽象方法和抽象类
2017/02/14 PHP
JavaScript 节点操作 以及DOMDocument属性和方法
2007/12/06 Javascript
JavaScript 版本自动生成文章摘要
2008/07/23 Javascript
location.href语句与火狐不兼容的问题
2010/07/04 Javascript
js之事件冒泡和事件捕获详细介绍
2013/10/28 Javascript
JavaScript不刷新实现浏览器的前进后退功能
2014/11/05 Javascript
理解javascript回调函数
2014/12/28 Javascript
JS脚本根据手机浏览器类型跳转WAP手机网站(两种方式)
2015/08/04 Javascript
javascript+HTML5的Canvas实现Lab单车动画效果
2015/08/07 Javascript
avalon js实现仿google plus图片多张拖动排序附源码下载
2015/09/24 Javascript
JS实现随页面滚动显示/隐藏窗口固定位置元素
2016/02/26 Javascript
如何处理JSON中的特殊字符
2016/11/30 Javascript
Bootstrap警告框(Alert)插件使用方法
2017/03/21 Javascript
详解angular2实现ng2-router 路由和嵌套路由
2017/03/24 Javascript
浅谈Emergence.js 检测元素可见性的 js 插件
2017/11/18 Javascript
webpack-dev-server自动更新页面方法
2018/02/22 Javascript
Element-ui之ElScrollBar组件滚动条的使用方法
2018/09/14 Javascript
vuex + axios 做登录验证 并且保存登录状态的实例
2018/09/16 Javascript
2种在vue项目中使用百度地图的简单方法
2018/09/28 Javascript
JavaScript一元正号运算符示例代码
2019/06/30 Javascript
Vuex的各个模块封装的实现
2020/06/05 Javascript
vue 遮罩层阻止默认滚动事件操作
2020/07/28 Javascript
React倒计时功能实现代码——解耦通用
2020/09/18 Javascript
微信小程序实现电影App导航和轮播
2020/11/30 Javascript
Python元字符的用法实例解析
2018/01/17 Python
Python+AutoIt实现界面工具开发过程详解
2019/08/07 Python
使用python和pygame制作挡板弹球游戏
2019/12/03 Python
python每5分钟从kafka中提取数据的例子
2019/12/23 Python
解决Python数据可视化中文部分显示方块问题
2020/05/16 Python
CSS3制作炫酷带方向感应的鼠标滑过图片3D动画
2016/03/16 HTML / CSS
详解CSS3选择器:nth-child和:nth-of-type之间的差异
2017/09/18 HTML / CSS
德国购买健身器材:AsVIVA
2017/08/09 全球购物
雨中的树观后感
2015/06/03 职场文书
python生成随机数、随机字符、随机字符串
2021/04/06 Python