SpringBoot中的异常处理与参数校验

Stella981
• 阅读 567

兄弟们好,这次来跟老铁交流两个问题,异常和参数校验,在说参数校验之前我们先来说异常处理吧,因为后面参数的校验会牵扯到异常处理这块的内容。

异常处理

说到异常处理,我不知道大家有没有写过或者遇到过如下的写法。

public void saveUser() {            try {        // 所有的业务内容,目测几百行    }catch (Exception e) {        e.printStackTrace();    }}

如果出现上述的代码,里面包含了大量的业务代码,如果是你写的,赶紧改掉,不是你写的找写的,吐槽赶紧改掉。

存在的问题:

  • 1、会遇到性能瓶颈;

  • 2、很难定位问题;

  • 3、try嵌套过多可读性很差;

不管什么原因出现了上述代码,那么最好还是改一下,如果非要在业务代码中try,那么也应该只在可能出现异常的地方使用try,而不是try整个业务代码。

SpringBoot中的异常捕获

直接上代码

@RestControllerAdvicepublic class GlobalException {    @ExceptionHandler(value = Exception.class) // 捕获的异常类型    public Object globalException(Exception ex) {        // 异常处理        ex.printStackTrace();        return "出现异常";    }}

那么在SpringBoot中我们就可以通过这样的一个配置可以获取到项目中出现异常的地方,我们可以在这个方法中可以获取出现异常的类的详细信息,那么是不是所有的异常我们全部使用Exception来处理呢?那么肯定是不合适的。

我们模拟一个by zero的异常,然后再配置一个处理ArithmeticException异常的处理器,代码如下:

@RestControllerAdvicepublic 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@RestControllerpublic 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里面的内容。

@RestControllerAdvicepublic 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@AllArgsConstructorpublic 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";}

最后的话

那么到这里,我们本篇文章就结束了,主要介绍了两部分内容,异常的处理和参数的校验。虽然很简单,但是我个人感觉还是挺常用的技能。所以与大家进行分享,如果对你有点帮助,就来点个赞吧。如果有什么不明白的也欢迎下方留言,一起来交流。

推荐阅读

字符串、集合如何判断空值?****看看成年人的正确操作

@PostConstruct注解,你该好好看看

SpringBoot中的异常处理与参数校验

本文分享自微信公众号 - 一个程序员的成长(xiaozaibuluo)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Easter79 Easter79
3年前
springmvc使用@Valid和@ControllerAdvise实现请求参数校验统一异常处理
springmvc使用@Valid和@ControllerAdvise实现请求参数校验统一异常处理(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fblog.ailijie.top%2Farchives%2Fspringmvcvalidcontrolleradvise)
Wesley13 Wesley13
3年前
Java8(5):使用 Optional 处理 null
Java8(5):使用Optional处理null写过Java程序的同学,一般都遇到过NullPointerException:)——为了不抛出这个异常,我们便会写如下的代码:UserusergetUserById(id);if(user!
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Stella981 Stella981
3年前
Spring Boot @ControllerAdvice+@ExceptionHandler处理controller异常
需求:  1.springboot 项目restful 风格统一放回json  2.不在controller写trycatch代码块简洁controller层  3.对异常做统一处理,同时处理@Validated校验器注解的异常方法:  @ControllerAdvice注解定义全局异常处理类@ControllerAdvice
Wesley13 Wesley13
3年前
初探 Objective
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言异常处理是许多高级语言都具有的特性,它可以直接中断当前函数并将控制权转交给能够处理异常的函数。不同语言在异常处理的实现上各不相同,本文主要来分析一下ObjectiveC和C这两个语言。为什么要把ObjectiveC和
Easter79 Easter79
3年前
SpringBoot过滤器中的异常处理
在昨天的文章我跟大家分享了SpringBoot中异常的处理中,我说了一个需要注意的点,就是过滤器中抛出的异常无法被异常处理类捕获,然后这个朋友就问应该如何处理。其实处理这种问题的处理方式有好几种,那么我就简单分享一下我近期一个项目中的处理方式。Filter中的异常处理思路首先我们要明白,在过滤器中我们一般是不会写很长
Wesley13 Wesley13
3年前
Java异常处理的最佳实践
Java异常处理的最佳实践为什么要有最佳实践我们在写程序是不可避免的要对代码进行异常处理,但是有时对异常的处理会使我们的程序变的更加糟糕,这是我们所不想看到的。所以,我们再进行异常处理时需要遵循一定的套路,来降低异常处理对我们程序的影响。异常产生的原因一般来说,java中的异常会
Easter79 Easter79
3年前
SpringBoot中的异常处理与参数校验
兄弟们好,这次来跟老铁交流两个问题,异常和参数校验,在说参数校验之前我们先来说异常处理吧,因为后面参数的校验会牵扯到异常处理这块的内容。异常处理说到异常处理,我不知道大家有没有写过或者遇到过如下的写法。publicvoidsaveUser(){try{//所有的业务内容,目测几百