前言

上一篇文章说到,参数校验,往往需要和全局的异常拦截器来配套使用,使得返回的数据结构永远是保持一致的。参数异常springboot默认的返回结构:

{
"timestamp": "2019-04-25T13:09:02.196+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Pattern.param.birthday",
"Pattern.birthday",
"Pattern.java.lang.String",
"Pattern"
],
"arguments": [
{
"codes": [
"param.birthday",
"birthday"
],
"arguments": null,
"defaultMessage": "birthday",
"code": "birthday"
},
[],
{
"defaultMessage": "\d{4}-\d{2}-\d{2}",
"codes": [
"\d{4}-\d{2}-\d{2}"
],
"arguments": null
}
],
"defaultMessage": "需要匹配正则表达式"\d{4}-\d{2}-\d{2}"",
"objectName": "param",
"field": "birthday",
"rejectedValue": "apple",
"bindingFailure": false,
"code": "Pattern"
}
],
"message": "Validation failed for object='param'. Error count: 1",
"path": "/validate/notblank"
}

不管是正常的情况,还是异常的情况,对于前端(或者app)来说,最好返回值的结构都是一致的,这样才方便解释。

定义一个BaseResult类,定义返回值的数据结构

public class BaseResult {
private int code;
private String message;
private Object data;
// 省略getter setter方法,全参构造方法
}

不管什么接口,都采用这样的数据结构返回给前端。比如约定code为0时是成功,其他错误定义出具体的错误码,message放错误信息,data对象放相应的数据。

定义全局异常处理器GlobalExceptionHandlerAdvice

@RestControllerAdvice
public class GlobalExceptionHandlerAdvice { }

使用RestControllerAdvice可以标识一个类为异常捕获类。

捕获异常

通过参数异常的测试,可以知道参数有异常时会抛出org.springframework.web.bind.MethodArgumentNotValidException。我们现在手动捕获 这个异常,并且返回一个BaseResult格式的响应。

@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
FieldError fieldError = result.getFieldError();
String defaultMessage = fieldError.getDefaultMessage();
return new BaseResult(11000, defaultMessage, null);
}

使用ExceptionHandler可以捕获异常类型。这里捕获了参数错误会抛出的异常,然后返回了自定义的结果。这里错误码为随便填写,真实开发,建议定义一个错误码枚举类。

效果如下:

返回的结果就比较友好了,前端处理起来也方便。

异常流处理业务逻辑

使用异常来处理业务逻辑,会使代码写起来更加流畅。比如说,一个删除用户数据的方法,返回值为void(无返回值),但是当传入的用户id不存在的时候,就应该返回一个用户不存在的结果,这对于void返回值的方法来说,显得无能为力。但是,使用异常流来处理该业务逻辑,会变得非常简单。我们直接抛出一个自定义异常,然后在异常捕获器上捕获该异常,再把结果返回给前端即可。

定义一个WebException

public class WebException extends RuntimeException {
private int code;
private String errorMsg;
public WebException(int code, String errorMsg) {
super(errorMsg);
this.code = code;
this.errorMsg = errorMsg;
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
// 省略getter setter方法
}

这里定义了一个运行时异常,重写了fillInStackTrace方法,重写该方法是不保留异常信息栈。因为我们使用该异常来处理业务逻辑,都是我们手动抛出的,所以也不需要保存异常信息栈了,这会提升性能。

在异常捕获器添加WebException异常捕获

@ExceptionHandler(WebException.class)
public BaseResult handleWebException(WebException e) {
return new BaseResult(e.getCode(), e.getErrorMsg(), null);
}

模拟一段业务逻辑,抛出WebException

在之前的UserController类,修改之前写的deleteUser方法,如下:

@DeleteMapping(value = "/{userId}")
public Object deleteUser(@PathVariable(value = "userId") Integer userId) {
if (userId == 0) {
throw new WebException(-1, "用户不存在");
}
return new BaseResult(1, "成功", null);
}

这里定义一个delete请求的接口,接收一个userId参数,如果userId等于0,则返回该用户不存在。测试结果如下:

当userId为0时,提示用户不存在

当userId为1时,提示成功.

总结

这里实现了全局异常捕获,并且介绍了异常流处理业务逻辑。这里只是一个小demo,还有很多待改进的地方。比如说,我没有定义一个错误码枚举类。在定义了错误码枚举类的前提下,修改构造BaseResult的模式,可以采用静态工厂模式来构造等。这里就不展开讨论了。

【快学springboot】5.全局异常捕获,异常流处理业务逻辑的更多相关文章

  1. springboot 全局异常捕获,异常流处理业务逻辑

    前言 上一篇文章说到,参数校验,往往需要和全局的异常拦截器来配套使用,使得返回的数据结构永远是保持一致的.参数异常springboot默认的返回结构: { "timestamp": ...

  2. 【快学springboot】12.实现拦截器

    前言 之前在[快学springboot]6.WebMvcConfigurer配置静态资源和解决跨域里有用到WebMvcConfigurer接口来实现静态资源的映射和解决跨域请求,并且在文末还说了Web ...

  3. 【快学springboot】4.接口参数校验

    前言 在开发接口的时候,参数校验是必不可少的.参数的类型,长度等规则,在开发初期都应该由产品经理或者技术负责人等来约定.如果不对入参做校验,很有可能会因为一些不合法的参数而导致系统出现异常. 上一篇文 ...

  4. 【快学springboot】8.JPA乐观锁OptimisticLocking

    介绍 当涉及到企业应用程序时,正确地管理对数据库的并发访问是至关重要的.为此,我们可以使用Java Persistence API提供的乐观锁定机制.它导致在同一时间对同一数据进行多次更新不会相互干扰 ...

  5. 【快学SpringBoot】Spring Cache+Redis实现高可用缓存解决方案

    前言 之前已经写过一篇文章介绍SpringBoot整合Spring Cache,SpringBoot默认使用的是ConcurrentMapCacheManager,在实际项目中,我们需要一个高可用的. ...

  6. 【快学springboot】13.操作redis之String数据结构

    前言 在之前的文章中,讲解了使用redis解决集群环境session共享的问题[快学springboot]11.整合redis实现session共享,这里已经引入了redis相关的依赖,并且通过spr ...

  7. 【快学springboot】9.使用 @Transactional 注解配置事务管理

    介绍 springboot对数据库事务的使用非常的方便,只需要在方法上添加@Transactional注解即可.Spring 为事务管理提供了丰富的功能支持.Spring 事务管理分为编程式和声明式的 ...

  8. 【快学springboot】1.快速创建springboot项目

    若图片查看异常,请前往掘金查看:https://juejin.im/post/5d00e793f265da1b614ff10b 使用spring initialize工具快速创建springboot项 ...

  9. 【快学springboot】SpringBoot整合Mybatis Plus

    原创声明 本文首发于头条号[Happyjava].Happy的掘金地址:https://juejin.im/user/5cc2895df265da03a630ddca,Happy的个人博客:http: ...

随机推荐

  1. plutosdr初步

    关于最近新出的pluto sdr ,一款较新的sdr,可以通过破解将ad9363改成ad9361,提升频率范围,但是据说没有办法改变带宽. 可以通过补丁方式使用某款软件来进行收听fm

  2. SVM资源

    算法源码: SVM-From-Scratch:https://github.com/adityajn105/SVM-From-Scratch

  3. hackme.inndy.tw - pyyy

    0x01 反编译 1.第一次尝试的时候我直接在线反编译,部分结果如下. for (i, f) in enumerate(F): n = pow(f, m, g) this_is = 'Y-Combin ...

  4. JS中bool值转换与比较

    前言 首先需要知道的是,js中有6个值为false,分别是: 0, '', null, undefined, NaN 和 false, 其他(包括{}, [], Infinity)为true. 可以使 ...

  5. 数据表损坏:Incorrect key file for table

    最近做项目过程中,调用数据库内容,老是出现一些类似于数据表损坏的提示信息(Incorrect key file for table edison_category),查询不到数据,很是恼火,后来冷静下 ...

  6. Perl 笔记

    目录 Perl 学习 常用记录 基础 1. 运行perl 2. 字符串 3. 变量 4. 条件 5. 循环 6. 运算符 7. 时间日期 8. 子程序(函数) 9. 引用 10. 格式化输出 11. ...

  7. 【代码总结】数据库抽象层PDO

    一.概述 PDO就是一个"数据库访问抽象层",起作用是统一各种数据库的访问接口,能够轻松的在不同数据库之间进行切换. 二.PDO的安装 编辑php.ini文件 添加 extensi ...

  8. javascript 原型继承 与class extends 继承对比

      //父类 Animal function Animal (name) { this.name = name; this.sleep = function () { console.log(this ...

  9. jQuery结合CSS实现手风琴组件

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. Python与线性代数——Numpy中的matrix()和array()的区别

    Numpy中matrix必须是2维的,但是 numpy中array可以是多维的(1D,2D,3D····ND).matrix是array的一个小的分支,包含于array.所以matrix 拥有arra ...