SpringBoot系列(十)优雅的处理统一异常处理与统一结果返回
SpringBoot系列(十)统一异常处理与统一结果返回
往期推荐
SpringBoot系列(一)idea新建Springboot项目
Springboot系列(七) 集成接口文档swagger,使用,测试
SpringBoot系列(八)分分钟学会Springboot多种解决跨域方式
目录
引言:
日常开发过程中,难免有的程序会因为某些原因抛出异常,而这些异常一般都是利用try ,catch的方式处理异常或者throw,throws的方式抛出异常不管。这种方法对于程序员来说处理也比较麻烦,对客户来说也不太友好,所以我们希望既能方便程序员编写代码,不用过多的自己去处理各种异常编写重复的代码又能提升用户的体验,这时候全局异常处理就显得很重要也很便捷了,是一种不错的选择。
1. 全局异常捕获与处理
因为现在主流的都是前后端分离的项目,所以我们的异常处理也根据前后端分离来讲述。
Springboot对于异常的处理也做了不错的支持,它提供了一个 @ControllerAdvice注解以及 @ExceptionHandler注解,前者是用来开启全局的异常捕获,后者则是说明捕获哪些异常,对那些异常进行处理。
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value =Exception.class)
public String exceptionHandler(Exception e){
System.out.println("发生了一个异常"+e);
return e.getMessage();
}
}
上面这段代码就是说,只要是代码运行过程中有异常就会进行捕获,并输出出这个异常。然后我们随便编写一个会发生异常的代码,测试出来的异常是这样的。

这对于我们前后端分离来说并不好,前后端分离之后唯一的交互就是json了,我们也希望将后端的异常变成json返回给前端处理。下面我们看看统一结果处理。
2. 统一结果返回与统一异常
代码:
public class Result<T> {
//是否成功
private Boolean success;
//状态码
private Integer code;
//提示信息
private String msg;
//数据
private T data;
public Result() {
}
//自定义返回结果的构造方法
public Result(Boolean success,Integer code, String msg,T data) {
this.success = success;
this.code = code;
this.msg = msg;
this.data = data;
}
//自定义异常返回的结果
public static Result defineError(DefinitionException de){
Result result = new Result();
result.setSuccess(false);
result.setCode(de.getErrorCode());
result.setMsg(de.getErrorMsg());
result.setData(null);
return result;
}
//其他异常处理方法返回的结果
public static Result otherError(ErrorEnum errorEnum){
Result result = new Result();
result.setMsg(errorEnum.getErrorMsg());
result.setCode(errorEnum.getErrorCode());
result.setSuccess(false);
result.setData(null);
return result;
}
}
说明:其中省略了get,set方法。另外方法之中包含了一个自定义的枚举。代码如下:
public enum ErrorEnum {
// 数据操作错误定义
SUCCESS(200, "nice"),
NO_PERMISSION(403,"你没得权限"),
NO_AUTH(401,"你能不能先登录一下"),
NOT_FOUND(404, "未找到该资源!"),
INTERNAL_SERVER_ERROR(500, "服务器跑路了"),
;
/** 错误码 */
private Integer errorCode;
/** 错误信息 */
private String errorMsg;
ErrorEnum(Integer errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public Integer getErrorCode() {
return errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
}
说明:枚举类中定义了常见的错误码以及错误的提示信息。这里我们就定义好了统一的结果返回,其中里面的静态方法是用来当程序异常的时候转换成异常返回规定的格式。
然后我们需要自定义异常处理类。代码如下:
public class DefinitionException extends RuntimeException{
protected Integer errorCode;
protected String errorMsg;
public DefinitionException(){
}
public DefinitionException(Integer errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
其中包含了错误的状态码,错误的提示信息。然后我们可以自定义一个全局异常处理类,来处理各种异常,包括自己定义的异常和内部异常。这样可以简化不少代码,不用自己对每个异常都使用try,catch的方式来实现。
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*
*/
@ExceptionHandler(value = DefinitionException.class)
@ResponseBody
public Result bizExceptionHandler(DefinitionException e) {
return Result.defineError(e);
}
/**
* 处理其他异常
*
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result exceptionHandler( Exception e) {
return Result.otherError(ErrorEnum.INTERNAL_SERVER_ERROR);
}
}
说明:每个方法上面加上一个 @ResponseBody的注解,用于将对象解析成json,方便前后端的交互,也可以使用 @ResponseBody放在异常类上面。
3. controller代码测试与结果
controller代码:
@RestController
@RequestMapping("/result")
public class ResultController {
@GetMapping("/getStudent")
public Result getStudent(){
Student student = new Student();
student.setAge(21);
student.setId(111);
student.setName("学习笔记");
Result result = new Result();
result.setCode(200);
result.setSuccess(true);
result.setData(student);
result.setMsg("学生列表信息");
return result;
}
@RequestMapping("/getDeException")
public Result DeException(){
throw new DefinitionException(400,"我出错了");
}
@RequestMapping("/getException")
public Result Exception(){
Result result = new Result();
int a=1/0;
return result;
}
}
其中的Student类就是前面一直在用的类了。包含三个属性。其中省略了get,set方法。
public class Student {
/**
* 唯一标识id
*/
private Integer id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
}
然后启动项目,来挨个测试。首先测试正常没有异常发生的数据。浏览器输入:localhost:8095/result/getStudent

可以看到数据是正常返回json串。没有异常。然后我们测试第二个自定义异常处理接口。浏览器输入localhost:8095/result/getDeException。

可以看到这个自定义的异常是捕获到了,并且返回了一个json串。最后我们测试一下其他的异常。浏览器输入:localhost:8095/result/getException

到这里我们就处理完了异常并且正确的返回了前端。
这里说一下,测试接口又很多方法,可以使用postman,或者idea自带的接口测试工具都很好用。
但是,你可能会发现一个问题,这种方法是不能处理404异常的,捕获不到。该怎么办呢?
4. 404异常特殊处理。
默认情况下,SpringBoot是不会抛出404异常的,所以@ControllerAdvice也不能捕获到404异常。我们可以通过以下配置来让这个注解能捕获到404异常。
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
其中第一句是表示:当发现404异常时直接抛出异常。第二句关闭默认的静态资源路径映射。这样404错误也能被捕获到,但是这个配置会让你的静态资源访问出现问题,也就是不适合前后端不分离的情况。
但是我们可以加上如下配置,就能正常访问静态资源了。
@Configuration
public class ResourceConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//可以访问localhost:8095/static/images/image.jpg
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
5. 总结
本文讲解了如何处理捕获全局异常以及怎么自定义异常,顺便说明了统一结果的返回格式,并特殊处理的404,not found的异常,将其作为统一结果返回。如果你觉得本文有用,点个赞吧!

SpringBoot系列(十)优雅的处理统一异常处理与统一结果返回的更多相关文章
- SpringBoot系列(十二)过滤器配置详解
SpringBoot(十二)过滤器详解 往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件 ...
- SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver
关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理: 本篇博文则带来另外一种并不常见的使用方式,通过实 ...
- SpringBoot系列教程web篇之全局异常处理
当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 Spring 项目中,可以怎样优雅的处 ...
- SpringBoot系列——@Async优雅的异步调用
前言 众所周知,java的代码是同步顺序执行,当我们需要执行异步操作时我们需要创建一个新线程去执行,以往我们是这样操作的: /** * 任务类 */ class Task implements Run ...
- Java 项目创建 -- 统一结果处理、统一异常处理、统一日志处理
一.IDEA 插件使用 1.说明 此处使用 SpringBoot 2.2.6 .JDK 1.8 .mysql 8.0.18 作为演示. 使用 IDEA 作为开发工具. 2.IDEA 插件 -- Lom ...
- SpringBoot系列: 如何优雅停止服务
============================背景============================在系统生命周期中, 免不了要做升级部署, 对于关键服务, 我们应该能做到不停服务完成 ...
- SpringBoot系列十:SpringBoot整合Redis
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合 Redis 2.背景 Redis 的数据库的整合在 java 里面提供的官方工具包:jed ...
- springboot系列十四、自定义实现starter
一.starter的作用 当我们实现了一个组建,希望尽可能降低它的介入成本,一般的组建写好了,只要添加spring扫描路径加载spring就能发挥作用.有个更简单的方式扫描路径都不用加,直接引入jar ...
- springboot系列十、springboot整合redis、多redis数据源配置
一.简介 Redis 的数据库的整合在 java 里面提供的官方工具包:jedis,所以即便你现在使用的是 SpringBoot,那么也继续使用此开发包. 二.redidTemplate操作 在 Sp ...
随机推荐
- 为什么我的PayPal 买家账号往商家账号付款,反而从商家账号里面扣款?
如果读者踩了跟我一样坑的话,建议赶紧去检查一下 application.properties 配置文件!!! 解决方法: 不妨试试将上面的 clientId和clientSecret 切换为你的商 ...
- Codeforces Round #625 (1A - 1D)
A - Journey Planning 题意: 有一列共 n 个城市, 每个城市有美丽值 b[i], 要访问一个子序列的城市, 这个子序列相邻项的原项数之差等于美丽值之差, 求最大的美丽值总和. ...
- Sublime Text 2 Install Package Debug
本文转载自CSDN空间freshlover的博客<Sublime Text 无法使用Package Control或插件安装失败的解决方法>,转载请注明出处,谢谢! Sublime Tex ...
- 深度使用JSON.stringify()
按照 JSON 的规范,使用 JSON.stringify() 做对象序列化时,如果一个属性为函数,那这个属性就会被忽略. const data1 = { a: 'aaa', fn: function ...
- POI2014 FAR-FarmCraft 树形DP+贪心
题目链接 https://www.luogu.org/problem/P3574 题意 翻译其实已经很明确了 分析 这题一眼就是贪心啊,但贪心的方法要思索一下,首先是考虑先走时间多的子树,但不太现实, ...
- 力软敏捷框架 jfGrid 的使用说明
很多人使用力软敏捷框架的一个困扰就是表格控件,力软并没有使用常规的jqgrid,而是用了自己的一套 jfgrid.所以今天在这做个简单的说明,如果你有什么疑问也可以在评论区提出来,后期的文章会做说明. ...
- Pytest系列(7) - skip、skipif跳过用例
如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 pytest.mark.sk ...
- PTA数据结构与算法题目集(中文) 7-18
PTA数据结构与算法题目集(中文) 7-18 7-18 银行业务队列简单模拟 (25 分) 设某银行有A.B两个业务窗口,且处理业务的速度不一样,其中A窗口处理速度是B窗口的2倍 —— 即当A窗 ...
- JMeter 接口测试 自动生成签名机制
在进行接口测试时,遇到接口进行了签名校验,为实现自动生成签名,经过一点研究终于成功. 首先,需要从前端获取 签名加密包 XXXsign.jar.. 建议将该jar包放在 jmeter lib 目录 ...
- Eclipse(Eclipse for android)代码自动提示设置
java代码和xml资源代码在有的eclipse中可以自己提示,但有的并不支持这个功能,还得我们人为去调整,主要原因是因为你们下载的elipse的渠道不同,获得的版本有的官方原版,有的是个人备份版等等 ...