SpringBoot系列(十)统一异常处理与统一结果返回

往期推荐

SpringBoot系列(一)idea新建Springboot项目

SpringBoot系列(二)入门知识

springBoot系列(三)配置文件详解

SpringBoot系列(四)web静态资源配置详解

SpringBoot系列(五)Mybatis整合完整详细版

SpringBoot系列(六)集成thymeleaf详解版

Springboot系列(七) 集成接口文档swagger,使用,测试

SpringBoot系列(八)分分钟学会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系列(十)优雅的处理统一异常处理与统一结果返回的更多相关文章

  1. SpringBoot系列(十二)过滤器配置详解

    SpringBoot(十二)过滤器详解 往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件 ...

  2. SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver

    关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理: 本篇博文则带来另外一种并不常见的使用方式,通过实 ...

  3. SpringBoot系列教程web篇之全局异常处理

    当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 Spring 项目中,可以怎样优雅的处 ...

  4. SpringBoot系列——@Async优雅的异步调用

    前言 众所周知,java的代码是同步顺序执行,当我们需要执行异步操作时我们需要创建一个新线程去执行,以往我们是这样操作的: /** * 任务类 */ class Task implements Run ...

  5. Java 项目创建 -- 统一结果处理、统一异常处理、统一日志处理

    一.IDEA 插件使用 1.说明 此处使用 SpringBoot 2.2.6 .JDK 1.8 .mysql 8.0.18 作为演示. 使用 IDEA 作为开发工具. 2.IDEA 插件 -- Lom ...

  6. SpringBoot系列: 如何优雅停止服务

    ============================背景============================在系统生命周期中, 免不了要做升级部署, 对于关键服务, 我们应该能做到不停服务完成 ...

  7. SpringBoot系列十:SpringBoot整合Redis

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合 Redis 2.背景 Redis 的数据库的整合在 java 里面提供的官方工具包:jed ...

  8. springboot系列十四、自定义实现starter

    一.starter的作用 当我们实现了一个组建,希望尽可能降低它的介入成本,一般的组建写好了,只要添加spring扫描路径加载spring就能发挥作用.有个更简单的方式扫描路径都不用加,直接引入jar ...

  9. springboot系列十、springboot整合redis、多redis数据源配置

    一.简介 Redis 的数据库的整合在 java 里面提供的官方工具包:jedis,所以即便你现在使用的是 SpringBoot,那么也继续使用此开发包. 二.redidTemplate操作 在 Sp ...

随机推荐

  1. App压力稳定性测试之Monkey

    一.Monkey简介 Android系统自带monkey程序,模拟用户触摸屏幕.滑动Trackball.按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常. Monkey的使用是在产 ...

  2. [leetcode] 树(Ⅱ)

    All questions are simple level. Construct String from Binary Tree Question[606]:You need to construc ...

  3. 三层架构之UI层

    之前已经发表了BLL,DAL,MODEL,三个层的源码 继续UI层: 先简单实现用户的登录及注册 高级操作可按照上一篇文章进行源码完善 如图所示↑ UI层目录文件 Reg.aspx 进行注册操作  & ...

  4. Q - 迷宫问题 POJ - 3984(BFS / DFS + 记录路径)

    Q - 迷宫问题 POJ - 3984 定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, ...

  5. Java String与char

    1. char类型 + char 类型 = 字符对应的ASCII码值相加(数字): char类型 + String 类型 = 字符对应的ASCII码值相加(数字) + String 类型: Strin ...

  6. Sqli-labs Less-58 报错注入 5次机会

    执行sql语句后,并没有返回数据库当中的数据,所以我们这里不能使用union联合注入,这里使用报错注入.但是需要注意这里只有5次机会尝试. 找表名 http://127.0.0.1/sql/Less- ...

  7. Flink| 实时需要分析

    ========================实时流量统计 1. 实时热门商品HotItems 每隔 5 分钟输出最近一小时内点击量最多的前 N 个商品. 抽取出业务时间戳,告诉 Flink 框架基 ...

  8. Elasticsearch 核心术语概念

    Elasticsearch 相当于一个关系型数据库 索引 index 类型 type 文档 document 字段 fields 跟关系型数据库对比 Elasticsearch 相当于一个数据库 索引 ...

  9. docker开机自动启动

    方法一: chkconfig docker on 方法二: 1.1是用systemctl: systemctl enable docker 1.2将Docker的docker.service服务移动到 ...

  10. Flask 入门(四)

    url反转 当我学习到url反转的时候,看了一个人写的例子,如下: from flask import Flask,url_for app = Flask(__name__)   @app.route ...