【快学springboot】5.全局异常捕获,异常流处理业务逻辑
前言
上一篇文章说到,参数校验,往往需要和全局的异常拦截器来配套使用,使得返回的数据结构永远是保持一致的。参数异常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.全局异常捕获,异常流处理业务逻辑的更多相关文章
- springboot 全局异常捕获,异常流处理业务逻辑
前言 上一篇文章说到,参数校验,往往需要和全局的异常拦截器来配套使用,使得返回的数据结构永远是保持一致的.参数异常springboot默认的返回结构: { "timestamp": ...
- 【快学springboot】12.实现拦截器
前言 之前在[快学springboot]6.WebMvcConfigurer配置静态资源和解决跨域里有用到WebMvcConfigurer接口来实现静态资源的映射和解决跨域请求,并且在文末还说了Web ...
- 【快学springboot】4.接口参数校验
前言 在开发接口的时候,参数校验是必不可少的.参数的类型,长度等规则,在开发初期都应该由产品经理或者技术负责人等来约定.如果不对入参做校验,很有可能会因为一些不合法的参数而导致系统出现异常. 上一篇文 ...
- 【快学springboot】8.JPA乐观锁OptimisticLocking
介绍 当涉及到企业应用程序时,正确地管理对数据库的并发访问是至关重要的.为此,我们可以使用Java Persistence API提供的乐观锁定机制.它导致在同一时间对同一数据进行多次更新不会相互干扰 ...
- 【快学SpringBoot】Spring Cache+Redis实现高可用缓存解决方案
前言 之前已经写过一篇文章介绍SpringBoot整合Spring Cache,SpringBoot默认使用的是ConcurrentMapCacheManager,在实际项目中,我们需要一个高可用的. ...
- 【快学springboot】13.操作redis之String数据结构
前言 在之前的文章中,讲解了使用redis解决集群环境session共享的问题[快学springboot]11.整合redis实现session共享,这里已经引入了redis相关的依赖,并且通过spr ...
- 【快学springboot】9.使用 @Transactional 注解配置事务管理
介绍 springboot对数据库事务的使用非常的方便,只需要在方法上添加@Transactional注解即可.Spring 为事务管理提供了丰富的功能支持.Spring 事务管理分为编程式和声明式的 ...
- 【快学springboot】1.快速创建springboot项目
若图片查看异常,请前往掘金查看:https://juejin.im/post/5d00e793f265da1b614ff10b 使用spring initialize工具快速创建springboot项 ...
- 【快学springboot】SpringBoot整合Mybatis Plus
原创声明 本文首发于头条号[Happyjava].Happy的掘金地址:https://juejin.im/user/5cc2895df265da03a630ddca,Happy的个人博客:http: ...
随机推荐
- 【MySQL】数据类型之字符相关
" 目录 字符类型 char类型 varchar类型 实测 总结 枚举类型与集合类型 字符类型 官网:https://dev.mysql.com/doc/refman/5.7/en/char ...
- min-25筛总结
怕忘了赶快更一下.就是求积性函数前缀和的. 没有 \(\LaTeX\) 原理 现在你有一个积性函数 f(1)=1 FP(p) FPK(p,k) 首先要求的是前缀和,那就是f(质数)+f(合数)+f(1 ...
- 大端(bigend)与小端(littleend)
大端:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中: 小端:是指数据的高位保存在内存的高地址中,而数据的高 ...
- 「HNOI2012」永无乡
传送门 Luogu 解题思路 很容易想到平衡树,然后还可以顺便维护一下连通性,但是如何合并两棵平衡树? 我们采用一种类似于启发式合并的思想,将根节点siz较小的那颗平衡树暴力的合并到另一颗上去. 那么 ...
- 喵星之旅-狂奔的兔子-redis使用
一.命令行使用 redis大概有200多命令,这里只是入门级别,列举了一些非常常见的内容,如果这些会了就可以开启redis进一步学习了. 1.登录数据库 我们需要知道ip地址.端口号.密码(如果有). ...
- js中的跨域
因为javascript的同源策略,导致它普通情况下不能跨域,直到现在,我还是不能完全理解js跨域的几种方法,没办法,只能慢慢学习,慢慢积累,这不,几天又在园里看到一篇博文,有所收获,贴上来看看; 原 ...
- Docker 安装 Kibana
使用和 elasticsearch 相同版本镜像 7.4.1 (不一样可能会出现问题) 1.下载Kibana镜像 # 下载Kibana镜像 docker pull kibana: # 查看镜像 do ...
- 基于springboot通过注解AOP动态切换druid多数据源--mybatis
控制于接口之上: 开始:demo地址 在lsr-core-base中 自定义注解: /** * @Description: 数据源切换注解 * @Package: lsr-microservice ...
- idea2018.3.6,离线使用maven的方法
(1)想办法拷贝联网电脑上已经下载好的仓库文件夹mavenRepository到离线环境的电脑上: (2)idea中按照如下图中配置: (3)打开 User settings file中设置的sett ...
- Nginx禁止使用ip访问,只允许使用域名访问
Nginx虚拟主机配置,vhosts下面有很多域名的配置: [root@external-lb01 vhosts]# pwd/data/nginx/conf/vhosts [root@external ...