当前位置: 首页 > news >正文

SpringBoot教程(二十七) | SpringBoot集成AOP实现异常处理

SpringBoot教程(二十七) | SpringBoot集成AOP实现异常处理

  • 前言
  • 第一步:统一接口返回结果
    • 1. 统一封装结果包含如下参数
    • 2. 创建 枚举HttpStatusEnum(返回结果代码)
    • 3. 创建 ResponseResult (返回实体类)
  • 第二步:配置全局异常处理
    • 1. 创建 GlobalException (全局异常处理类)
    • 2. 自定义异常类(用于主动返回业务性错误)
    • 3. 如何使用自定义的异常类
      • 1. 使用 @RestControllerAdvice
      • 2. 不使用 @RestControllerAdvice

前言

捕获全局异常后进行相关处理后,肯定是需要返回对象给前端的。
为了统一规范化,提高交互的规范性及通用性 及 前后端联调效率。
避免一个接口一个返回格式的问题。
所以需要完成以下两步
1.统一接口返回结果
2.配置全局异常处理

第一步:统一接口返回结果

1. 统一封装结果包含如下参数

  • 状态码:code
  • 状态信息:status
  • 返回信息:message
  • 数据:data

2. 创建 枚举HttpStatusEnum(返回结果代码)

import lombok.Getter;/*** Http状态返回枚举***/
@Getter
public enum HttpStatusEnum {/*** 操作成功*/SUCCESS(200, "操作成功"),/*** 对象创建成功*/CREATED(201, "对象创建成功"),/*** 请求已经被接受*/ACCEPTED(202, "请求已经被接受"),/*** 操作已经执行成功,但是没有返回数据*/NO_CONTENT(204, "操作已经执行成功,但是没有返回数据"),/*** 资源已被移除*/MOVED_PERM(301, "资源已被移除"),/*** 重定向*/SEE_OTHER(303, "重定向"),/*** 资源没有被修改*/NOT_MODIFIED(304, "资源没有被修改"),/*** 参数列表错误(缺少,格式不匹配)*/BAD_REQUEST(400, "参数列表错误(缺少,格式不匹配)"),/*** 未授权*/UNAUTHORIZED(401, "未授权"),/*** 访问受限,授权过期*/FORBIDDEN(403, "访问受限,授权过期"),/*** 资源,服务未找到*/NOT_FOUND(404, "资源,服务未找!"),/*** 不允许的http方法*/BAD_METHOD(405, "不允许的http方法"),/*** 资源冲突,或者资源被锁*/CONFLICT(409, "资源冲突,或者资源被锁"),/*** 不支持的数据,媒体类型*/UNSUPPORTED_TYPE(415, "不支持的数据,媒体类型"),/*** 系统内部错误*/ERROR(500, "系统内部错误"),/*** 接口未实现*/NOT_IMPLEMENTED(501, "接口未实现"),/*** 系统警告消息*/WARN(601,"系统警告消息");private final Integer code;private final String message;HttpStatusEnum(Integer code, String message) {this.code = code;this.message = message;}public Integer getCode() {return code;}public String getMessage() {return message;}
}

3. 创建 ResponseResult (返回实体类)

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 返回结果集***/
@Data
//生成一个包含所有字段的构造函数
@AllArgsConstructor
//生成一个无参构造函数
@NoArgsConstructor
public class ResponseResult<T> {/*** 状态码*///@ApiModelProperty(value = "状态码")private Integer code;/*** 状态信息*///@ApiModelProperty(value = "状态信息")private Boolean status;/*** 返回信息*///@ApiModelProperty(value = "返回信息")private String message;/*** 数据*///@ApiModelProperty(value = "数据")private T data;/*** 全参数方法** @param code    状态码* @param status  状态* @param message 返回信息* @param data    返回数据* @param <T>     泛型* @return {@link ResponseResult<T>}*/private static <T> ResponseResult<T> response(Integer code, Boolean status, String message, T data) {ResponseResult<T> responseResult = new ResponseResult<>();responseResult.setCode(code);responseResult.setStatus(status);responseResult.setMessage(message);responseResult.setData(data);return responseResult;}/*** 全参数方法** @param code    状态码* @param status  状态* @param message 返回信息* @param <T>     泛型* @return {@link ResponseResult<T>}*/private static <T> ResponseResult<T> response(Integer code, Boolean status, String message) {ResponseResult<T> responseResult = new ResponseResult<>();responseResult.setCode(code);responseResult.setStatus(status);responseResult.setMessage(message);return responseResult;}/*** 成功返回(无参)** @param <T> 泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> success() {return response(HttpStatusEnum.SUCCESS.getCode(), true, HttpStatusEnum.SUCCESS.getMessage(), null);}/*** 成功返回(枚举参数)** @param httpResponseEnum 枚举参数* @param <T>              泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> success(HttpStatusEnum httpResponseEnum) {return response(httpResponseEnum.getCode(), true, httpResponseEnum.getMessage());}/*** 成功返回(状态码+返回信息)** @param code    状态码* @param message 返回信息* @param <T>     泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> success(Integer code, String message) {return response(code, true, message);}/*** 成功返回(返回信息 + 数据)** @param message 返回信息* @param data    数据* @param <T>     泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> success(String message, T data) {return response(HttpStatusEnum.SUCCESS.getCode(), true, message, data);}/*** 成功返回(状态码+返回信息+数据)** @param code    状态码* @param message 返回信息* @param data    数据* @param <T>     泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> success(Integer code, String message, T data) {return response(code, true, message, data);}/*** 成功返回(数据)** @param data 数据* @param <T>  泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> success(T data) {return response(HttpStatusEnum.SUCCESS.getCode(), true, HttpStatusEnum.SUCCESS.getMessage(), data);}/*** 成功返回(返回信息)** @param message 返回信息* @param <T>  泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> success(String message) {return response(HttpStatusEnum.SUCCESS.getCode(), true, message, null);}/*** 失败返回(无参)** @param <T> 泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> fail() {return response(HttpStatusEnum.ERROR.getCode(), false, HttpStatusEnum.ERROR.getMessage(), null);}/*** 失败返回(枚举)** @param httpResponseEnum 枚举* @param <T>              泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> fail(HttpStatusEnum httpResponseEnum) {return response(httpResponseEnum.getCode(), false, httpResponseEnum.getMessage());}/*** 失败返回(状态码+返回信息)** @param code    状态码* @param message 返回信息* @param <T>     泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> fail(Integer code, String message) {return response(code, false, message);}/*** 失败返回(返回信息+数据)** @param message 返回信息* @param data    数据* @param <T>     泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> fail(String message, T data) {return response(HttpStatusEnum.ERROR.getCode(), false, message, data);}/*** 失败返回(状态码+返回信息+数据)** @param code    状态码* @param message 返回消息* @param data    数据* @param <T>     泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> fail(Integer code, String message, T data) {return response(code, false, message, data);}/*** 失败返回(数据)** @param data 数据* @param <T>  泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> fail(T data) {return response(HttpStatusEnum.ERROR.getCode(), false, HttpStatusEnum.ERROR.getMessage(), data);}/*** 失败返回(返回信息)** @param message 返回信息* @param <T>  泛型* @return {@link ResponseResult<T>}*/public static <T> ResponseResult<T> fail(String message) {return response(HttpStatusEnum.ERROR.getCode(), false, message, null);}
}

第二步:配置全局异常处理

1. 创建 GlobalException (全局异常处理类)

关键注解:@RestControllerAdvice 可以直接使用,不需要与@Configuration配合使用

package com.example.reactboot.aop;import cn.hutool.core.util.ObjectUtil;
import com.example.reactboot.Exception.ServiceException;
import com.example.reactboot.zdyresult.HttpStatusEnum;
import com.example.reactboot.zdyresult.ResponseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.servlet.http.HttpServletRequest;
import java.nio.file.AccessDeniedException;/*** 异常处理 配置** @author javadog*/
@RestControllerAdvice
public class GlobalException {private static final Logger log = LoggerFactory.getLogger(GlobalException.class);/*** 权限校验异常*/@ExceptionHandler(AccessDeniedException.class)public ResponseResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {String requestURI = request.getRequestURI();log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());return ResponseResult.fail(HttpStatusEnum.FORBIDDEN.getCode(), HttpStatusEnum.FORBIDDEN.getMessage());}/*** 请求方式不支持*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)public ResponseResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,HttpServletRequest request) {String requestURI = request.getRequestURI();log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());return ResponseResult.fail(e.getMessage());}/*** 业务异常(自定义的)*/@ExceptionHandler(BusinessException.class)public ResponseResult handleServiceException(BusinessException e) {log.error(e.getMessage(), e);Integer code = e.getCode();return ObjectUtil.isNotNull(code) ? ResponseResult.fail(code, e.getMessage()) : ResponseResult.fail(e.getMessage());}/*** 拦截未知的运行时异常*/@ExceptionHandler(RuntimeException.class)public ResponseResult handleRuntimeException(RuntimeException e, HttpServletRequest request) {String requestURI = request.getRequestURI();log.error("请求地址'{}',发生未知异常.", requestURI, e);return ResponseResult.fail(e.getMessage());}/*** 系统异常*/@ExceptionHandler(Exception.class)public ResponseResult handleException(Exception e, HttpServletRequest request) {String requestURI = request.getRequestURI();log.error("请求地址'{}',发生系统异常.", requestURI, e);return ResponseResult.fail(e.getMessage());}/*** 数据绑定异常*/@ExceptionHandler(BindException.class)public ResponseResult handleBindException(BindException e) {log.error(e.getMessage(), e);String message = e.getAllErrors().get(0).getDefaultMessage();return ResponseResult.fail(message);}/*** 方法参数验证失败异常*/@ExceptionHandler(MethodArgumentNotValidException.class)public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {log.error(e.getMessage(), e);String message = e.getBindingResult().getFieldError().getDefaultMessage();return ResponseResult.fail(message);}}

2. 自定义异常类(用于主动返回业务性错误)

主动返回业务性错误,错误状态及描述想自由发挥。
就可以使用这个自定义的异常来操作

package com.example.reactboot.Exception;/*** 自定义的服务异常类*/
public class BusinessException extends RuntimeException {private static final long serialVersionUID = 1L; // 用于序列化private Integer code; // 错误码字段// 构造函数,可以接收一个消息字符串和一个错误码public ServiceException(String message, Integer code) {super(message);this.code = code;}// 构造函数,只接收一个消息字符串public ServiceException(String message) {this(message, null); // 默认错误码为null,表示没有提供}// 构造函数,接收一个消息字符串和一个原因(Throwable),以及一个错误码public ServiceException(String message, Throwable cause, Integer code) {super(message, cause);this.code = code;}// 构造函数,只接收一个原因(Throwable)和一个错误码public ServiceException(Throwable cause, Integer code) {super(cause);this.code = code;}// getter方法public Integer getCode() {return code;}// setter方法(如果需要的话,但通常异常对象一旦创建就不应该修改其状态)public void setCode(Integer code) {this.code = code;}
}

3. 如何使用自定义的异常类

@RestController
public class LoginController {@GetMapping("exception")public String second(){System.out.println("开始测试自定义的异常类");throw new BusinessException("用户名密码错误",688);}
}

执行完以后,控制台报错信息为
在这里插入图片描述

1. 使用 @RestControllerAdvice

使用 @RestControllerAdvice ,报错后异常被其捕获,postman调用请求显示为
Stutas是200
在这里插入图片描述

2. 不使用 @RestControllerAdvice

不使用 @RestControllerAdvice ,不存在异常捕获,postman调用请求显示为
Stutas是500
在这里插入图片描述

参考文章
【1】【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Kubernetes 1.20 上将容器从 Docker Engine 改为 Containerd
  • 视频智能分析打手机检测算法安防监控打手机检测算法应用场景、算法源码、算法模型介绍
  • 【杭州】目前就业情况-自述
  • Docker安装Neo4j图数据库和APOC插件
  • 指尖疯2024年下半年软考报名快报:赛程过半,你报名成功了吗?
  • Python爬虫案例四:爬取某个博主的所有文章保存成PDF格式
  • 好文分类汇总
  • leetcode209. Minimum Size Subarray Sum
  • MATLAB下的粒子滤波例程|三维非线性模型|组合导航|PF代码(无需下载,直接复制到MATLAB上即可运行)
  • 带你玩转nova Flip的百变趣方屏,直观感受趣味与实用的“刚刚好”
  • C++:二叉树进阶
  • 科普小课堂:中等硬度的床垫,合适的睡姿,通过日常力量练习提升自身能力以支撑脊柱形态。
  • 线性代数 第四讲 极大线性无关组,等价向量组,向量组的秩
  • wordpress在北美华人中的使用情况分析
  • SpringBoot中使用Redis-Jedis
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • ES6核心特性
  • maven工程打包jar以及java jar命令的classpath使用
  • spring-boot List转Page
  • Swoft 源码剖析 - 代码自动更新机制
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 基于web的全景—— Pannellum小试
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 面试总结JavaScript篇
  • 微信小程序设置上一页数据
  • 温故知新之javascript面向对象
  • 正则与JS中的正则
  • 智能合约Solidity教程-事件和日志(一)
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (BFS)hdoj2377-Bus Pass
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (C语言)球球大作战
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (pojstep1.1.2)2654(直叙式模拟)
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (计算机网络)物理层
  • (每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?
  • (三)uboot源码分析
  • (转)jQuery 基础
  • (转)visual stdio 书签功能介绍
  • (转)详解PHP处理密码的几种方式
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • .apk文件,IIS不支持下载解决
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET CLR Hosting 简介
  • .net mvc部分视图
  • .NET NPOI导出Excel详解
  • .Net 应用中使用dot trace进行性能诊断
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .NET高级面试指南专题十一【 设计模式介绍,为什么要用设计模式】