GlobalExceptionHandler 自定义异常类 + 处理validation的异常

在 Spring Boot 项目中,​自定义异常通常用于处理特定的业务逻辑错误,并结合全局异常处理器(@ControllerAdvice)统一返回结构化的错误信息。

一.全局异常处理器: 

1. 自定义异常类

定义一个继承自 RuntimeException 的业务异常类,包含错误码和错误信息:

public class BusinessException extends RuntimeException {private final int code; // 自定义错误码private final String message; // 错误信息public BusinessException(int code, String message) {super(message);this.code = code;this.message = message;}// 使用枚举定义错误类型(推荐)public BusinessException(ErrorCode errorCode) {super(errorCode.getMessage());this.code = errorCode.getCode();this.message = errorCode.getMessage();}// Getterpublic int getCode() { return code; }@Override public String getMessage() { return message; }
}
配套的枚举:
public enum ErrorCode {USER_NOT_FOUND(1001, "用户不存在"),INVALID_PARAM(1002, "参数无效"),INTERNAL_ERROR(5000, "系统内部错误");private final int code;private final String message;ErrorCode(int code, String message) {this.code = code;this.message = message;}// Getterpublic int getCode() { return code; }public String getMessage() { return message; }
}

2. 全局异常处理器

使用 @ControllerAdvice 捕获异常并统一返回 JSON 格式的错误响应:

@Slf4j
@ControllerAdvice  // 标记该类为全局异常处理器,可以拦截所有Controller抛出的异常
@ResponseBody // 或直接使用 @RestControllerAdvice
public class GlobalExceptionHandler {/​**​* 处理业务异常*/@ExceptionHandler(BusinessException.class) // 处理自定义异常BusinessException@ResponseStatus(HttpStatus.BAD_REQUEST) // 指定HTTP响应的状态码 400 错误public Result<Void> handleBusinessException(BusinessException e) {log.error("业务异常: code={}, message={}", e.getCode(), e.getMessage());return Result.fail(e.getCode(), e.getMessage());}/​**​* 处理系统异常(兜底)*/@ExceptionHandler(Exception.class)  // 处理所有未被其他处理器捕获的异常@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 指定HTTP响应的状态码 500 错误public Result<Void> handleException(Exception e) {log.error("系统异常: ", e);return Result.fail(ErrorCode.INTERNAL_ERROR.getCode(), "系统繁忙,请稍后重试");}
}
统一的响应封装类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {private int code;private String message;private T data;// 成功响应public static <T> Result<T> success(T data) {return new Result<>(200, "成功", data);}// 失败响应public static <T> Result<T> fail(int code, String message) {return new Result<>(code, message, null);}
}

3. 在业务代码中抛出异常:

@RestController
@RequestMapping("/user")
public class UserController {@GetMapping("/{id}")public Result<User> getUser(@PathVariable Long id) {User user = userService.findById(id);if (user == null) {throw new BusinessException(ErrorCode.USER_NOT_FOUND); // 抛出业务异常}return Result.success(user);}
}

4.使用方法:

throw new BusinessException(1001, "用户不存在");
throw new BusinessException(ErrorCode.USER_NOT_FOUND);

二.对于JSR303校验的异常处理:

早在JavaEE6规范中就定义了参数校验的规范,它就是JSR-303,它定义了Bean Validation,即对bean属性进行校验。

SpringBoot提供了JSR-303的支持,它就是spring-boot-starter-validation,它的底层使用Hibernate Validator,Hibernate Validator是Bean Validation 的参考实现。

所以,我们准备在Controller层使用spring-boot-starter-validation完成对请求参数的基本合法性进行校验。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

在javax.validation.constraints包下有很多这样的校验注解,直接使用注解定义校验规则即可。

例子如下:

package com.xuecheng.content.model.dto;import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.ToString;/*** @description 添加课程dto* @author Mr.M* @date 2022/9/7 17:40* @version 1.0*/
@Data
@ToString
@Schema(name = "AddCourseDto", description = "新增课程基本信息")
public class AddCourseDto {@NotEmpty(message = "课程名称不能为空")@Schema(description = "课程名称", required = true)private String name;@NotEmpty(message = "适用人群不能为空")@Size(message = "适用人群内容过少",min = 10)@Schema(description = "适用人群", required = true)private String users;@NotEmpty(message = "课程分类不能为空")@Schema(description = "大分类", required = true)private String mt;@NotEmpty(message = "课程分类不能为空")@Schema(description = "小分类", required = true)private String st;@NotEmpty(message = "课程等级不能为空")@Schema(description = "课程等级", required = true)private String grade;@Schema(description = "教学模式(普通,录播,直播等)", required = true)private String teachmode;@Schema(description = "课程介绍")private String description;@Schema(description = "课程图片", required = true)private String pic;@NotEmpty(message = "收费规则不能为空")@Schema(description = "收费规则,对应数据字典", required = true)private String charge;
}

 上边用到了@NotEmpty和@Size两个注解,@NotEmpty表示属性不能为空,@Size表示限制属性内容的长短。

定义好校验规则还需要开启校验,在controller方法中添加@Validated注解,如下:

@Operation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody @Validated AddCourseDto addCourseDto){//机构id,由于认证系统没有上线暂时硬编码Long companyId = 1L;return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}

如果校验出错Spring会抛出MethodArgumentNotValidException异常,我们需要在统一异常处理器中捕获异常,解析出异常信息。

@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {// 拿到校验框架BindingResult bindingResult = e.getBindingResult();List<String> msgList = new ArrayList<>();// 将错误信息放在msgListbindingResult.getFieldErrors().stream().forEach(item->msgList.add(item.getDefaultMessage()));// 将msgList的错误拼接String msg = StringUtils.join(msgList, ",");log.error("【系统异常】{}",msg);return new RestErrorResponse(msg);
}

有时候在同一个属性上设置一个校验规则不能满足要求,比如:订单编号由系统生成,在添加订单时要求订单编号为空,在更新订单时要求订单编写不能为空。

此时就用到了分组校验,同一个属性定义多个校验规则属于不同的分组,比如:添加订单定义@NULL 规则属于 insert 分组,更新订单定义@NotEmpty规则属于 update 分组,insert 和update 是分组的名称,是可以修改的。

下边举例说明,我们用class类型来表示不同的分组,所以我们定义不同的接口类型(空接口)表示不同的分组,由于校验分组是公用的,所以定义在 base工程中。如下:

package com.xuecheng.base.execption;/*** @description 校验分组* @version 1.0*/
public class ValidationGroups {public interface Inster{};public interface Update{};public interface Delete{};}

下边在定义校验规则时指定分组:

@NotEmpty(groups = {ValidationGroups.Inster.class},message = "添加课程名称不能为空")
@NotEmpty(groups = {ValidationGroups.Update.class},message = "修改课程名称不能为空")
// @NotEmpty(message = "课程名称不能为空")
@ApiModelProperty(value = "课程名称", required = true)
private String name;

在Controller方法中启动校验规则指定要使用的分组名:

@Operation("新增课程基础信息")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody @Validated({ValidationGroups.Inster.class}) AddCourseDto addCourseDto){//机构id,由于认证系统没有上线暂时硬编码Long companyId = 1L;return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}

再次测试,由于这里指定了Insert分组,所以抛出 异常信息:添加课程名称不能为空。

如果修改分组为ValidationGroups.Update.class,异常信息为:修改课程名称不能为空。

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

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

相关文章

软件测试过程中如何定位BUG

在软件测试过程中&#xff0c;定位BUG是确保软件质量的关键环节。有效的BUG定位不仅能帮助开发人员快速修复问题&#xff0c;还能提升整个软件项目的效率。以下是软件测试中定位BUG的系统性方法和策略&#xff1a; 一、复现BUG 步骤&#xff1a; 收集信息&#xff1a;记录BUG…

如何优化Elasticsearch的搜索性能?

优化 Elasticsearch 的搜索性能需要从索引设计、查询优化、硬件配置和集群调优等多方面入手。以下是系统化的优化策略和实操建议: 一、索引设计优化 1. 合理设置分片数 分片大小:单个分片建议 10-50GB(超过50GB会影响查询性能)。分片数量: 总分片数 ≤ 节点数 1000(避免…

台式电脑CPU天梯图_2025年台式电脑CPU天梯图

CPU的选择绝对是重中之重,它关乎了一台电脑性能好坏。相信不少用户,在挑选CPU的时候不知道谁强谁弱,尤其是intel和AMD两款CPU之间。下面通过2025年台式电脑CPU天梯图来了解下这两款cpu. 2025年台式电脑CPU天梯图 2025年台式电脑CPU天梯图包含了老旧型号以及12代、13代、14代…

HarmonyOS_ArkTs_API(1)

HarmonyOS_ArkTs_API(1) 概述 此API服务模块是独自开发的应用程序的核心骨架&#xff0c;提供了鸿蒙OS ArkTS客户端组件和Java Spring Boot后端之间的强大通信接口。该模块采用清晰的架构方法处理所有HTTP请求、响应解析和错误处理&#xff0c;确保系统各部分间通信的一致性和…

matlab雷达定位仿真

一、边扫描边跟踪雷达仿真 边扫描边跟踪&#xff08;BISTAR&#xff09;雷达仿真是一种实时雷达信号处理的技术&#xff0c;用于模拟雷达系统的操作过程&#xff0c;特别是那些具备连续扫描能力的雷达。它的基本原理和流程可以分为以下几个步骤&#xff1a; &#xff08;1&…

互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景

一&#xff1a;并发 1.1MySQL并发事务访问相同记录 &#xff08;1&#xff09;读-读 不影响 &#xff08;2&#xff09;写-写 写的数据需要一个一个来&#xff0c;排队执行 &#xff08;3&#xff09;读-写 两次读…

KEYSIGHT N9320B是德科技N9320B频谱分析仪

KEYSIGHT N9320B是德科技N9320B频谱分析仪 附加功能&#xff1a; 频率范围&#xff1a;9 kHz 至 3 GHz 分辨率带宽&#xff1a;10 Hz 至 1 MHz DANL&#xff1a;-130 dBm&#xff0c;-148 dBm&#xff0c;带可选前置放大器 整体幅度精度&#xff1a;<1.5 dB 最小非零扫…

零基础开始的网工之路第十四天------Linux程序管理

目录 一、Linux程序与进程 1、程序,进程,线程的概念 2、程序和进程的区别 3、进程和线程的区别 二、Linux进程基础(生命周期) 1、进程生命周期 2、父子进程的关系 三、程序管理 1、常见的软件包类型 四、Linux操作系统启动流程详解 1、概述 2、启动流程核心阶段 1…

群辉(synology)NAS老机器连接出现网页端可以进入,但是本地访问输入一样的账号密码是出现错误时解决方案

群辉&#xff08;synology&#xff09;NAS老机器连接出现网页端可以进入&#xff0c;但是本地访问输入一样的账号密码是出现错误时解决方案 老机器 装的win7 系统 登入后端网页端的时候正常&#xff0c;但是本地访问登入时输入登入网页端一样的密码时候出现问题解决方案 1.登…

单例模式的隐秘危机

引言 单例模式作为设计模式中的基石&#xff0c;广泛应用于配置管理、线程池、缓存系统等关键场景。然而&#xff0c;许多开发者误以为“私有构造函数”足以保障其唯一性&#xff0c;却忽视了反射机制、对象克隆、序列化反序列化这三把“隐形利刃”——它们能绕过常规防御&…

DMBOK对比知识点对比(3)

1.数据仓库建设方法(Inmon、Kimball) 数据仓库建设方法(Inmon、Kimball)P293方法

Python+VR:如何让虚拟世界更懂你?——用户行为分析的实践

友友们好! 我是Echo_Wish,我的的新专栏《Python进阶》以及《Python!实战!》正式启动啦!这是专为那些渴望提升Python技能的朋友们量身打造的专栏,无论你是已经有一定基础的开发者,还是希望深入挖掘Python潜力的爱好者,这里都将是你不可错过的宝藏。 在这个专栏中,你将会…

游戏引擎学习第311天:支持手动排序

仓库: https://gitee.com/mrxiao_com/2d_game_7(已满) 新仓库: https://gitee.com/mrxiao_com/2d_game_8 回顾并为今天的内容定下基调 我们接下来要继续完成之前开始的工作&#xff0c;上周五开始的部分内容&#xff0c;虽然当时对最终效果还不太确定&#xff0c;但现在主要任…

数据结构第2章绪论 (竟成)

第 2 章 绪论 本章主要介绍数据结构相关的一些基本概念&#xff0c;是后续章节的基础。我们也将 408 考试大纲中&#xff0c;关于数据结构部分的考查目标罗列在这里&#xff0c;供各位考生参考&#xff1a; 1.掌握数据结构的基本概念、基本原理和基本方法。 2.掌握数据的逻辑结…

spring boot 拦截器HandlerInterceptor 不生效的原因排查

public class UserInterceptor implements HandlerInterceptor项目添加一个拦截器&#xff0c;发现未生效 1、排查拦截本身是否注入了springbean 容器 Slf4j Component public class LoginInterceptor implements HandlerInterceptor {2、排查springboot 项目扫描范围是否包含…

用Python绘制动态爱心:代码解析与浪漫编程实践

用Python绘制动态爱心:代码解析与浪漫编程实践 一、摘要二、整体架构流程1. 初始化配置模块2. 几何绘制引擎3. 动画控制系统4. 辅助功能模块三、技术细节解析1. Turtle性能优化2. 数学公式应用3. 颜色渐变算法4. 异常处理机制5. 动画节奏控制四、结论与展望附:完整代码一、摘…

WPS 免登录解锁编辑

遇到 WPS 需要登录才能启用编辑功能&#xff1f; 如何免登录使用编辑功能&#xff1f; 方法一 解锁方法 1、关闭 WPS&#xff1b; 2、桌面右键→ “新建”→“文本文档”&#xff0c;粘贴以下内容&#xff08;见最下面&#xff09;&#xff1b;编码保持默认&#xff08;ANSI …

ORDER BY子句在一个 SQL 查询中只能出现一次

order by A.create_time,A.update_time desc和 order by A.create_time desc,A.update_time desc有区别吗&#xff1f; 关键区别 第一个排序中 create_time 是升序(默认是ASC)&#xff0c;第二个是降序(DESC) 只有在 DESC 关键字紧跟在列名后面时&#xff0c;该列才会按降序排…

02-BTC-密码学原理 对hash算法如果出现漏洞的思考

如果比特币中某个哈希函数的抗碰撞性出现了漏洞怎么办&#xff0c;怎么补救&#xff1f; 答&#xff1a;&#xff08;1&#xff09;攻击场景&#xff1a; 伪造交易&#xff1a;攻击者可构造两个不同的交易&#xff08;如正常交易和恶意双花交易&#xff09;具有相同的TxID&…

特征分解:线性代数在AI大模型中的核心工具

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…