SpringBoot中的异常处理与参数校验
兄弟们好,这次来跟老铁交流两个问题,异常和参数校验,在说参数校验之前我们先来说异常处理吧,因为后面参数的校验会牵扯到异常处理这块的内容。
异常处理
说到异常处理,我不知道大家有没有写过或者遇到过如下的写法。
public void saveUser() { try { // 所有的业务内容,目测几百行 }catch (Exception e) { e.printStackTrace(); } }
如果出现上述的代码,里面包含了大量的业务代码,如果是你写的,赶紧改掉,不是你写的找写的,吐槽赶紧改掉。
存在的问题:
- 1、会遇到性能瓶颈;
- 2、很难定位问题;
- 3、try嵌套过多可读性很差;
不管什么原因出现了上述代码,那么最好还是改一下,如果非要在业务代码中try,那么也应该只在可能出现异常的地方使用try,而不是try整个业务代码。
SpringBoot中的异常捕获
直接上代码
@RestControllerAdvice public class GlobalException { @ExceptionHandler(value = Exception.class) // 捕获的异常类型 public Object globalException(Exception ex) { // 异常处理 ex.printStackTrace(); return "出现异常"; } }
那么在SpringBoot中我们就可以通过这样的一个配置可以获取到项目中出现异常的地方,我们可以在这个方法中可以获取出现异常的类的详细信息,那么是不是所有的异常我们全部使用Exception来处理呢?那么肯定是不合适的。
我们模拟一个by zero的异常,然后再配置一个处理ArithmeticException异常的处理器,代码如下:
@RestControllerAdvice public class GlobalException { @ExceptionHandler(value = Exception.class) // 捕获的异常类型 public Object globalException(Exception ex) { ex.printStackTrace(); return "出现异常"; } @ExceptionHandler(value = ArithmeticException.class) public Object arithmeticException(ArithmeticException ex) { ex.printStackTrace(); return "by zero异常"; } }
如果这个时候出现by zero异常,走ArithmeticException异常处理,原因就是因为如果有更小范围的异常处理类,那么会走小范围的异常处理器。不会走globalException更大的异常处理类。
这样处理之后,我们就不需要在项目中去写那么多的try了,是不是方便了很多。
除了使用这些已经存在的异常外,其实我们还可以自定义我们的异常,比如我们常用的用户未登录异常、参数错误异常等等。但是考虑到这篇文章的篇幅问题,这次就先不写了,有兴趣的朋友可以直接下面留言,人多了我尽快更新。
注意坑:
这里跟大家分享一个踩过的坑,不能再Filter过滤器中抛出异常,如果通过在过滤器中抛出异常,然后通过异常处理类来处理,那么是不可能的,因为处理器是捕获不到Filter抛出的异常的。
参数校验
老规矩,先来看一段代码
@RequestMapping(value = "/save/user") public Object saveUser(UserPO userPO) { if (userPO.getAge() == null) { return "请求参数错误"; } if (userPO.getSex() == null) { return "请求参数错误"; } if (userPO.getUsername() == null) { return "请求参数错误"; } // ... return "SUCCESS"; }
应该见过这种校验参数的吧,说实话我写过。越写感觉越low,所以狠心一下,还是趁早改吧。
@Validated注解
这个注解其实是Spring提供的,如果你的项目不是SpringBoot项目,需要引一下需要的pom文件,如果是,那么就不用管了,SpringBoot已经帮我们引入了。
网上看了好多的博客,许多都说的不是很全,大部分都是说JavaBean参数的校验,但是我们项目中有些接口可能就涉及一个参数,根本不需要写一个JavaBean,对于单一参数的校验好多博客还是没说的,那么我们这次就一次性讲清楚。
单一参数的校验
直接看代码吧
@Validated @RestController public class BookController { @RequestMapping(value = "/book/info", method = RequestMethod.GET) public Object getBookInfo(@NotBlank(message = "书籍ID不能为空") String bookId) { return "SUCCESS"; } }
这里要跟大家特别说明下,如果是单一参数的校验,那么我们必须要在类上面添加@Validated注解,不然我们整个单个参数校验是不会生效的,可以看到我们在校验参数bookId的时候,使用了@NotBlank那么顾名思义,就是这个参数不能为null,在调用了trim()方法之后也不能是空字符。
如果参数不满足要求,那么会抛出ConstraintViolationException异常,这个异常只有在单一参数校验的时候抛出,如果你的参数是JavaBean,那么就不是这个异常了。
既然我们知道了它会抛出异常,并且我们也知道是什么异常类型,那么久超级简单了,我们可以直接使用上面刚学的异常处理类来处理我们的异常。
我找个里面写的比较简单,如果你想写的复杂一点,其实也是可以的,但是作为后端来说,我觉得没必要,因为我们不能给前端提示太过明显的错误提示,防止别人恶意攻击我们,就像用户名密码错误,不能明确的告诉用户到底是用户名错误还是密码错误,只能提示用户名或密码错误。
如果大家非要把详细的错误信息打出来,要看到到底是哪个参数校验不通过,也可以通过下面的方式将具体的参数错误信息打印出来。输出的错误结果其实就是上面message里面的内容。
@RestControllerAdvice public class ExceptionCatch { /** * 单个参数异常处理 * * @param ex * @return */ @ExceptionHandler(value = ConstraintViolationException.class) public Object constraintViolationException(ConstraintViolationException ex) { // 获取具体的错误信息 Set<ConstraintViolation<?>> violations = ex.getConstraintViolations(); // 打印数据 violations.forEach(e -> System.out.println(e.getMessage())); return "单个-请求参数错误"; } }
JavaBean参数校验(form-data)
JavaBean的写法
@Data @NoArgsConstructor @AllArgsConstructor public class UserPO { @NotBlank(message = "用户名不能为空") private String username; @NotNull(message = "年龄不能为空") @Min(value = 1, message = "年龄最小为1") @Max(value = 200, message = "年龄最大为200") private Integer age; @NotBlank(message = "性别不能为空") private String sex; }
Controller写法
@RequestMapping(value = "/save/user") public Object saveUser(@Validated UserPO userPO) { // ... return "SUCCESS"; }
跟单一参数校验不一样的是JavaBean的校验方式需要将@Validated写在方法参数,而不是类上。如果出现了参数校验不通过,同样的也会抛出一个异常,BindException。
/** * 一般参数校验绑定异常处理 * * @param ex * @return */ @ExceptionHandler(value = BindException.class) public Object bindException(BindException ex) { BindingResult bindingResult = ex.getBindingResult(); // 获取所有的错误信息 List<ObjectError> allErrors = bindingResult.getAllErrors(); // 输出 allErrors.forEach(e -> System.out.println(e.getDefaultMessage())); return "请求参数错误"; }
注意:大家要注意post请求有两种方式,一种是基于form-data格式的数据传递,另外一种就是基于json格式的数据传递,两种传递方式引发的异常也是不一样的,所以我们还要单独处理基于json的参数校验异常处理。
JavaBean参数校验(json)
我们先来看下Controller接收方式
@RequestMapping(value = "/save/user") public Object saveUser(@Validated @RequestBody UserPO userPO) { // ... return "SUCCESS"; }
对应的参数异常处理
/** * JSON参数校验绑定异常处理 * * @param ex * @return */ @ExceptionHandler(value = MethodArgumentNotValidException.class) public Object methodArgumentNotValidException(MethodArgumentNotValidException ex) { BindingResult bindingResult = ex.getBindingResult(); // 获取所有的错误信息 List<ObjectError> allErrors = bindingResult.getAllErrors(); // 输出 allErrors.forEach(e -> System.out.println(e.getDefaultMessage())); return "请求参数错误-json"; }
最后的话
那么到这里,我们本篇文章就结束了,主要介绍了两部分内容,异常的处理和参数的校验。虽然很简单,但是我个人感觉还是挺常用的技能。所以与大家进行分享,如果对你有点帮助,就来点个赞吧。如果有什么不明白的也欢迎下方留言,一起来交流。
更多内容请关注微信公众号:一个程序员的成长
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
好程序员Python培训分享Python面试宝典之基础篇-03
好程序员Python培训分享Python面试宝典之基础篇-03 题目011:Python中为什么没有函数重载?点评:C++、Java、C#等诸多编程语言都支持函数重载,所谓函数重载指的是在同一个作用域中有多个同名函数,它们拥有不同的参数列表(参数个数不同或参数类型不同或二者皆不同),可以相互区分。重载也是一种多态性,因为通常是在编译时通过参数的个数和类型来确定到底调用哪个重载函数,所以也被称为编译时多态性或者叫前绑定。这个问题的潜台词其实是问面试者是否有其他编程语言的经验,是否理解Python是动态类型语言,是否知道Python中函数的可变参数、关键字参数这些概念。首先Python是解释型语言,函数重载现象通常出现在编译型语言中。其次Python是动态类型语言,函数的参数没有类型约束,也就无法根据参数类型来区分重载。再者Python中函数的参数可以有默认值,可以使用可变参数和关键字参数,因此即便没有函数重载,也要可以让一个函数根据调用者传入的参数产生不同的行为。题目012:用Python代码实现Python内置函数max。点评:这个题目看似简单,但实际上还是比较考察面试者的功底。因为P...
- 下一篇
Seata 1.2.0 重磅发布,支持 XA 事务模式
Seata 1.2.0 Seata 1.2.0 发布。 Seata 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。 此版本更新如下: feature: [#2381] 支持 XA 事务模式 [#2206] 支持 REQUIRED、REQUIRES_NEW、SUPPORTS 和 NOT_SUPPORTED 事务传播模式 [#2112] 支持 SQL 的批量更新和批量删除 [#2275] TCC 模式支持 HSF 调用 [#2108] 支持 zip、bzip2、7z 压缩 [#2328] server 支持 mysql5.x 和 mysql8.x 类隔离加载 [#2367] 支持 Nacos 1.2 权限配置 [#2359] 支持 NEVER、MANDATORY 事务传播模式 和 事务挂起恢复 API [#2418] 支持 fst 序列化 [#2135] 支持 SPI 定义 scope [#2370] 支持 failureHandler 从 Spring 容器注入 [#2481] 支持数据库连接池的 max-wait 配置 [#2379] 支持使用 Nacos 注册...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker快速安装Oracle11G,搭建oracle11g学习环境