SpringBoot教程(九) | SpringBoot统一异常处理

SpringBoot教程(九) | SpringBoot统一异常处理

异常大家应该都很清楚,我们的项目总是不可避免的出现异常,那么应该如何优雅的进行异常处理使我们需要关注的一个问题,合理的异常封装既可以方便前端的处理,也能够简化后端的开发。

一般情况下,我们应该在我们的项目中,根据不同的异常场景,定义不同的异常类型,然后不同的异常类型,返回不同的状态码,然后和前端约定好,根据不同的状态码,做不同的展现。

SpringBoot中为我们提供一个统一的异常处理类,也是利用了AOP的思想,我们可以向外抛出各种类型的异常,然后在这个统一的处理类中,针对每一种不同类型的异常,做不同的数据封装,返回给前端。

代码编写:主要就是通过一个 @ControolerAdvice注解,实现对所有请求的拦截,很像AOP。

(注意,下面的代码仅供展示,如果大家直接粘贴,可能需要引入一些三方jar包才行)

java复制代码@RestControllerAdvice
@Order(1)
public class GlobalExceptionHandler {private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);public GlobalExceptionHandler() {}@ExceptionHandler({ParamException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class, BindException.class, HttpMessageNotReadableException.class, MissingServletRequestPartException.class, MissingServletRequestParameterException.class, MultipartException.class})public Result<?> paramsExceptionHandler(HttpServletRequest request, Exception e) {String msg;if (e instanceof MethodArgumentNotValidException) {MethodArgumentNotValidException ex = (MethodArgumentNotValidException)e;msg = this.handlerErrors(ex.getBindingResult());} else if (e instanceof BindException) {BindException ex = (BindException)e;msg = this.handlerErrors(ex.getBindingResult());} else if (e instanceof ConstraintViolationException) {ConstraintViolationException ex = (ConstraintViolationException)e;Optional<ConstraintViolation<?>> first = ex.getConstraintViolations().stream().findFirst();msg = (String)first.map(ConstraintViolation::getMessage).get();} else {msg = e.getMessage();}Result<?> result = Result.error(ResultCode.PARAM_ERROR.getCode(), msg);return this.printLogAndReturn(request, result, e);}private String handlerErrors(BindingResult bindingResult) {List<FieldError> errors = bindingResult.getFieldErrors();FieldError error = (FieldError)errors.get(0);return error.getDefaultMessage();}@ExceptionHandler({BizException.class})public Result<?> bizExceptionHandler(HttpServletRequest request, BizException e) {Result<?> result = Result.error(e.getCode() == null ? ResultCode.BIZ_ERROR.getCode() : e.getCode(), e.getMessage());return this.printLogAndReturn(request, result, e);}@ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class})public Result<?> httpRequestMethodNotSupportedExceptionHandler(HttpServletRequest request, Exception e) {Result<?> result = Result.error(ResultCode.REQ_MODE_NOT_SUPPORTED);return this.printLogAndReturn(request, result, e);}@ExceptionHandler({JSONException.class})public Result<?> jsonExceptionHandler(HttpServletRequest request, Exception e) {Result<?> result = Result.error(ResultCode.JSON_FORMAT_ERROR);return this.printLogAndReturn(request, result, e);}@ExceptionHandler({DataAccessException.class})public Result<?> sqlExceptionHandler(HttpServletRequest request, Exception e) {Result<?> result = Result.error(ResultCode.SQL_ERROR);return this.printLogAndReturn(request, result, e);}@ExceptionHandler({Exception.class})@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public Result<?> exceptionHandler(HttpServletRequest request, Exception e) {Result<?> result = Result.error(ResultCode.SYS_ERROR);return this.printLogAndReturn(request, result, e);}private Result<?> printLogAndReturn(HttpServletRequest request, Result<?> result, Exception e) {String requestUrl = request.getRequestURL().toString() + (StringUtil.isEmpty(request.getQueryString()) ? "" : "?" + request.getQueryString());log.error("<-异常返回-> 请求接口:{} | 异常时间:{} | 异常结果:{}", new Object[]{requestUrl, System.currentTimeMillis(), JSON.toJSONString(result)});log.error("<--异常堆栈信息-->");log.error(Throwables.getStackTraceAsString(e));return result;}
}

@ExceptionHandler 标识对哪种异常进行拦截。这里可以有我们自己定义的异常。当我们在业务代码中有一些异常处理的时候,我们可以根据具体的业务场景,将其抛出为我们自己定义的异常,然后在统一的异常处理类中,根据不同的异常类型,返回我们统一封装的结果。

大家可以看看上面的代码,对于所有的错误都封装成了Result对象,并且打印了异常的信息。

好的,接下来我们来写一个案例。首先把前面的统一结果的封装加入到项目中

  1. 在exception 自定义一个业务异常类
java复制代码public class BizException extends RuntimeException {private Integer code;public BizException() {}public BizException(String message) {super(message);}public BizException(Integer code, String message) {super(message);this.code = code;}public BizException(ResultCode resultCode) {super(resultCode.getMsg());this.code = resultCode.getCode();}public BizException(String message, Throwable cause) {super(message, cause);}public BizException(int code, String message, Throwable cause) {super(message, cause);this.code = code;}public BizException(ResultCode resultCode, Throwable cause) {super(resultCode.getMsg(), cause);this.code = resultCode.getCode();}public Integer getCode() {return this.code;}public void setCode(Integer code) {this.code = code;}
}
  1. 然后在把刚才的异常处理类也加到exception包下。
java复制代码@RestControllerAdvice
@Order(1)
public class GlobalExceptionHandler {private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);public GlobalExceptionHandler() {}private String handlerErrors(BindingResult bindingResult) {List<FieldError> errors = bindingResult.getFieldErrors();FieldError error = (FieldError)errors.get(0);return error.getDefaultMessage();}@ExceptionHandler({BizException.class})public Result<?> bizExceptionHandler(HttpServletRequest request, BizException e) {Result<?> result = Result.error(e.getCode() == null ? ResultCode.BIZ_ERROR.getCode() : e.getCode(), e.getMessage());return this.printLogAndReturn(request, result, e);}@ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class})public Result<?> httpRequestMethodNotSupportedExceptionHandler(HttpServletRequest request, Exception e) {Result<?> result = Result.error(ResultCode.REQ_MODE_NOT_SUPPORTED);return this.printLogAndReturn(request, result, e);}@ExceptionHandler({Exception.class})@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public Result<?> exceptionHandler(HttpServletRequest request, Exception e) {Result<?> result = Result.error(ResultCode.SYS_ERROR);return this.printLogAndReturn(request, result, e);}private Result<?> printLogAndReturn(HttpServletRequest request, Result<?> result, Exception e) {ObjectMapper mapper = new ObjectMapper();String requestUrl = request.getRequestURL().toString() + (!StringUtils.hasLength(request.getQueryString()) ? "" : "?" + request.getQueryString());try {log.error("<-异常返回-> 请求接口:{} | 异常时间:{} | 异常结果:{}", new Object[]{requestUrl, System.currentTimeMillis(), mapper.writeValueAsString(result)});} catch (JsonProcessingException jsonProcessingException) {jsonProcessingException.printStackTrace();}log.error("<--异常堆栈信息-->");StringWriter stringWriter = new StringWriter();e.printStackTrace(new PrintWriter(stringWriter));log.error(stringWriter.toString());return result;}
}

接下来我们就可以在程序中使用。

  1. 开发一个Controller进行测试,直接抛出异常
java复制代码@RestController
public class ThirdExceptionController {@GetMapping("exception")public User second(){System.out.println(1);throw new BizException(ResultCode.BIZ_ERROR.getCode(), "用户名密码错误");}}

记得传token,因为有我们的拦截器。

image.png

异常成功按照我们想要的格式返回了。当然我们可以在抛出异常的时候,自己的定义我们的code和message, 其实还可以和Assert联合使用,让代码更加的优雅。

我们同时修改一下我们之前的拦截器,之前的拦截器在拦截token的时候,如果没传token就直接返回false这种方式不是很友好,因为在浏览器上看到就是一个空白页。http请求不会继续执行,我们可以在这里不返会false,而是直接封装一个我们自己定义的异常。

java复制代码@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 核心业务逻辑,判断是否登录等String token = request.getHeader("token");// 正常token是的登录后签发的,前端携带过来if(!StringUtils.hasLength(token)){throw new BizException(9001, "token不能为空");}return true;}

浏览器验证效果:

image.png 好了关于异常的处理我们就讲解到这里,希望对你有帮助。

另: 配套项目代码已托管中gitCode: gitcode.net/lsqingfeng/…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/624888.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stable Diffusion XL(SDXL)核心基础知识

文章目录 一、Stable Diffusion XL基本概念二、SDXL模型架构上的优化&#xff08;一&#xff09;SDXL的整体架构&#xff08;二&#xff09;VAE&#xff08;三&#xff09;U-Net&#xff08;四&#xff09;text encoder&#xff08;五&#xff09;refiner model 三、SDXL在训练…

软件测试|Python如何处理配置文件

配置文件在软件开发中起到了非常重要的作用&#xff0c;它允许开发者将应用程序的设置和参数存储在一个易于管理和修改的地方&#xff0c;而不是硬编码在代码中。Python有多种处理配置文件的方式&#xff0c;本文将介绍其中两种最常用的方法&#xff1a;使用configparser库和使…

HTML--CSS--超链接样式以及鼠标样式自定义

超链接伪类 再复习一下,超链接的定义方式如下&#xff1a; <!DOCTYPE html> <html> <head> <title>这是一个标题</title><meta charset"utf-8"/><style></style> </head> <body><a href"http…

Axure RP软件揭秘:设计师的秘密武器

Axure rp是一种快速原型设计工具&#xff0c;可以制作高度互动的HTML原型。设计师不仅可以使用Axure绘制线框图和原型&#xff0c;还可以在Axure rp中完成一系列用户体验设计。在本文中&#xff0c;我们将根据用户体验设计师的真实经验&#xff0c;触发用户体验设计师的实际工作…

如何实现本地USB设备共享服务映射到外网实现跨网USB共享通信访问

文章目录 前言1. 安装下载软件1.1 内网安装使用USB Redirector1.2 下载安装cpolar内网穿透 2. 完成USB Redirector服务端和客户端映射连接3. 设置固定的公网地址 前言 USB Redirector是一款方便易用的USB设备共享服务应用程序&#xff0c;它提供了共享和访问本地或互联网上的U…

vue的element ui使用el-table组件实现懒加载树、默认自动展开层级(一层,二层)、并且解决新增、删除、修改之后树节点不刷新问题

1.整体思路 问题&#xff1a;数据量太大了&#xff0c;导致接口返回数据时间较长。解决: 将ElementUi中Table组件加载改为懒加载&#xff08;查看文档&#xff09;。思路&#xff1a;初始化打开页面时只显示第一级菜单,用户点击展开菜单之后往后端发送请求,然后加载出一级子菜…

适当催一下没确认订单的国外客户

在一片美丽的森林里&#xff0c;住着两只小鸟。其中一只非常勤奋&#xff0c;每天都早早起床&#xff0c;练习飞翔和觅食。而另外一个小鸟却非常懒惰&#xff0c;每天总是赖在窝里&#xff0c;不愿意努力&#xff0c;懒惰的小鸟总是想&#xff1a;反正有那只勤奋的鸟儿在&#…

【Linux】初识Linux及几个基本指令

Hello everybody!算算时间我已经有一个多月没有更新啦&#xff01;因为本专业是纺织工程&#xff0c;所以一直在复习应付期末考试\(0^◇^0)/。那好&#xff0c;废话不多说。让我们进入今天的主题&#xff01; 关于Linux系统可能很多同学不是很熟悉&#xff0c;有的人可能听过&…

如何在网络爬虫中解决CAPTCHA?使用Python进行网络爬虫

网络爬虫是从网站提取数据的重要方法。然而&#xff0c;在进行网络爬虫时&#xff0c;常常会遇到一个障碍&#xff0c;那就是CAPTCHA&#xff08;全自动公共图灵测试以区分计算机和人类&#xff09;。本文将介绍在网络爬虫中解决CAPTCHA的最佳方法&#xff0c;并重点介绍CapSol…

软件测试|使用matplotlib绘制多种饼图

简介 Matplotlib是一个强大的数据可视化库&#xff0c;它允许我们创建各种类型的图表&#xff0c;包括饼图。饼图是一种用于显示数据分布的常见图表类型。在本文中&#xff0c;我们将介绍如何使用Matplotlib创建不同类型的饼图&#xff0c;并提供示例代码。 创建标准饼图 首…

Linux进程【2】进程地址空间(+页表详解哦)

fork 引言&#xff08;程序地址空间&#xff09;进程地址空间进程地址空间mm_struct 虚拟地址到物理地址的转化总结 引言&#xff08;程序地址空间&#xff09; 在之前的学习过程中&#xff0c;我们认识了内存与地址&#xff0c;并且了解了在程序地址空间中的基本分区&#xf…

2000年第五次人口普查数据,shp/excel格式均有,划分年龄段、性别占比等字段

基本信息. 数据名称: 第五次人口普查数据 数据格式: Shp、excel 数据时间: 2000年 数据几何类型: 面 数据坐标系: WGS84坐标系 数据来源&#xff1a;第五次人口普查数据 数据字段&#xff1a; 序号字段名称字段说明1a2000_zrks2000年_常住人口&#xff08;人&…

Eureka 本机集群实现

距离上次发布博客已经一年多了&#xff0c;主要就是因为考研&#xff0c;没时间学习技术的内容&#xff0c;现在有时间继续完成关于代码方面的心得&#xff0c;希望跟大家分享。 今天在做一个 Eureka 的集群实现&#xff0c;我是在本电脑上跑的&#xff0c;感觉这个挺有意思&a…

Linux的网络文件共享服务之FTP服务

一.存储类型 1.1 存储类型分为三种 直连式存储&#xff1a;Direct-Attached Storage&#xff0c;简称DAS 存储区域网络&#xff1a;Storage Area Network&#xff0c;简称SAN&#xff08;可以使用空间&#xff0c;管理也是你来管理&#xff09; 网络附加存储&#xff1a;Net…

Sqoop作业调度:自动化数据传输任务

自动化数据传输任务是大数据处理中的一个重要方面&#xff0c;可以定期执行Sqoop作业&#xff0c;确保数据在不同系统之间的同步。本文将深入探讨如何使用Sqoop作业调度来自动化数据传输任务&#xff0c;并提供详细的示例代码和全面的内容&#xff0c;以帮助大家更好地理解和应…

二叉树简介

二叉树 二叉树是每个节点最多有两个子树的树结构&#xff0c;通常子树被称作“左子树”和“右子树”。 二叉树的遍历 二叉树的遍历主要有三种方式&#xff1a;前序遍历、中序遍历和后序遍历。 前序遍历&#xff1a;访问根节点 --> 遍历左子树 --> 遍历右子树中序遍历&…

三、MyBatis 多表映射

本章概要 多表映射概念对一映射对多映射多表映射总结 多表映射优化多表映射总结 3.1 多表映射概念 多表查询结果映射思路 开发中有很多** 多表查询**需求&#xff0c;这种情况如何让进行处理&#xff1f; MyBatis 思想是&#xff1a;数据库不可能永远是你所想或所需的那个样…

用LED数码管显示计数器T0的计数值

#include<reg51.h> //包含51单片机寄存器定义的头文件 sbit SP3^2 ; //将S位定义为P3.2引脚 unsigned char Tab[ ]{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //段码表 unsigned char x; /************************************************************…

Arduino开发实例-手指心率传感器模块

手指心率传感器模块 文章目录 手指心率传感器模块1、手指心率传感器介绍2、硬件准备及接线3、代码实现1、手指心率传感器介绍 本次使用的心率传感器模块是为教育和娱乐目的而设计的,通过手指检测心血管脉搏波。 它使用 PPG (HRM-2511E) 探头进行数据传输。 该传感器使用红外线…

Mysql中设置只允许指定ip能连接访问(可视化工具的方式)

场景 Mysql中怎样设置指定ip远程访问连接&#xff1a; Mysql中怎样设置指定ip远程访问连接_navicat for mysql 设置只有某个ip可以远程链接-CSDN博客 前面设置root账户指定ip能连接访问是通过命令行的方式&#xff0c;如果通过可视化工具比如Navicat来实现。 注&#xff1a…