SpringBoot项目实战:自定义异常和统一参数验证(附源码)

news2025/2/7 16:07:22

你好,我是田哥

在实际开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类BusinessException

自定义异常和统一校验参数已用于 充电桩项目 中。

自定义异常

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年06月11日 22:41
 * 博客地址:<a href="http://woaijava.cc/">博客地址</a>
 * <p>
 * 自定义异常
 */
@Getter
public class BusinessException extends RuntimeException {
    /**
     * http状态码
     */
    private int code;

    private Object object;

    public BusinessException(String message, int code, Object object) {
        super(message);
        this.code = code;
        this.object = object;
    }

    public BusinessException(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.object = resultCode.getMessage();
    }

    public BusinessException(ResultCode resultCode, String message) {
        this.code = resultCode.getCode();
        this.object = message;
    }

    public BusinessException(String message) {
        super(message);
    }
}

一些常见需要对参数进校验,项目中自定义了一个校验工具类ParamValidate

public class ParamValidate {

    public static void isNull(Object object, String message) {
        if (object == null) {
            throw new BusinessException(ResultCode.PARAMETER_EMPTY, message);
        }
    }

    public static void isTrue(boolean expression, String message) {
        if (!expression) {
            throw new BusinessException(ResultCode.PARAMETER_ERROR, message);
        }
    }

    public static void notEmpty(Collection collection, String message) {
        isNull(collection, message);
        if (collection.size() == 0) {
            throw new BusinessException(ResultCode.PARAMETER_ERROR, message);
        }
    }
}

工具使用:

@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private MessageTemplateSingleton messageTemplateSingleton;

    @GetMapping("/index1")
    public CommonResult<MessageTemplateSingleton.Template> index() {
        ParamValidate.isNull(null, "参数为空");
        MessageTemplateSingleton.Template template = messageTemplateSingleton.getTemplate(1);
        return CommonResult.success(template);
    }
}

自定义异常统一处理:

@RestControllerAdvice
@Slf4j
public class ChargeStationAdvice {

    @ExceptionHandler(Exception.class)
    public CommonResult<String> doException(Exception e) {

        log.error("统一处理自定义异常机制,触发异常 msg ", e);
        String message = null;
        int errorCode = ResultCode.FAILED.getCode();
        //自定义异常
        if (e instanceof BusinessException) {
            BusinessException exception = (BusinessException) e;
            message = exception.getMessage();
            errorCode = exception.getCode();
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            message = "不支持GET/POST方法";
        } else if (e instanceof NoHandlerFoundException) {
            message = "请求接口不存在";
        }  else{
             message = "系统异常";
        }
        return CommonResult.failed(errorCode, message);
    }
}

测试:

GET http://localhost:9001/test/index1

返回:

{
  "code": 400007,
  "message": null,
  "data": null
}

6f5d67d9bcd831da45c3da7676e29e80.png

到此我们的自定义异常的定义、处理、测试就搞定了。

但是,我们在上面使用到的是统一异常处理,我们在方法参数验证时候,也会用到统一异常处理。

统一参数验证

我们后台使用spring 为我们提供好的统一校验的工具spring-boot-starter-validation对请求进行校验。

pom依赖:

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

这里通过注解封装了几种常用的校验

  • @NotNull 不能为null

  • @NotEmpty 不能为null、空字符串、空集合

  • @NotBlank 不能为null、空字符串、纯空格的字符串

  • @Min 数字最小值不能小于x

  • @Max 数字最大值不能大于x

  • @Email 字符串为邮件格式

  • @Max 数字最大值不能大于x

  • @Size 字符串长度最小为x、集合长度最小为x

  • @Pattern 正则表达式

因为校验不通过时,会抛出各种异常,所以,我们需要对这些异常进行统一处理。

@RestControllerAdvice
@Slf4j
public class ChargeStationAdvice {

    @ExceptionHandler(Exception.class)
    public CommonResult<String> doException(Exception e) {

        log.error("统一异常处理机制,触发异常 msg ", e);
        String message = null;
        int errorCode = ResultCode.FAILED.getCode();
        //自定义异常
        if (e instanceof BusinessException) {
            BusinessException exception = (BusinessException) e;
            message = exception.getMessage();
            errorCode = exception.getCode();
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            message = "不支持GET/POST方法";
        } else if (e instanceof NoHandlerFoundException) {
            message = "请求接口不存在";
        } else if (e instanceof MissingServletRequestParameterException) {
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = String.format("缺少必要参数[%s]", ((MissingServletRequestParameterException) e).getParameterName());
        } else if (e instanceof MethodArgumentNotValidException) {
            BindingResult result = ((MethodArgumentNotValidException) e).getBindingResult();
            FieldError error = result.getFieldError();
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = error == null ? ResultCode.PARAMETER_ERROR.getMessage() : error.getDefaultMessage();
        } else if (e instanceof BindException) {
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = e.getMessage();
        } else if (e instanceof ConstraintViolationException) {
            Optional<ConstraintViolation<?>> first = ((ConstraintViolationException) e).getConstraintViolations().stream().findFirst();
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = first.get().getMessage();
        }
        return CommonResult.failed(errorCode, message);
    }
}

上面几个异常进行解释说明:

MissingServletRequestParameterException :加了@RequestParam注解,但是接口调用时没有传指定的参数(注意:是没有传,而不是传了,但是值是null)。

MethodArgumentNotValidException :经过测试,当校验的参数放在对象中,接口的请求方式是post请求,用@Valid @RequestBody方式接受参数时,如果报错,会被该捕获器捕获。

BindException :经过测试,当校验参数写在类中,接口请求方式是get请求时,报错会被该捕获器捕获。

ConstraintViolationException :传了值,但是不符合要求。@NotNull(message = “最大值不能为空”) @Min(value = 10,message = "参数必须大于10"),要求传非null值,且值必须大于10,否则会返回错误信息。经过测试,当校验参数直接写在接口上,而不是写在类中,报错会被该捕获器捕获。

参数校验实体类:

import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年05月09日 20:03
 * 博客地址:<a href="http://woaijava.cc/">博客地址</a>
 */
@Data
public class TestDto implements Serializable {
    @NotNull
    @Min(value = 10,message = "参数必须大于10")
    private Long id;
    @NotEmpty(message = "name参数不能为空")
    private String name;
}

注意:每个注解对应的包路径。

我们在Controller层使用TestDto,并使用@Valid注解,使校验的注解生效:

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private MessageTemplateSingleton messageTemplateSingleton; 

    @PostMapping("/index2")
    public CommonResult<MessageTemplateSingleton.Template> index2(@RequestBody @Valid TestDto testDto) {
        log.info("入参={}", testDto);
        MessageTemplateSingleton.Template template = messageTemplateSingleton.getTemplate(1);
        return CommonResult.success(template);
    }
}

注意:注解 @Valid 的全路径为:javax.validation.Valid;

测试:

POST http://localhost:9001/test/index2

Content-Type: application/json; charset=UTF-8

{   "id": 11,   "name": "" }

返回:

{
  "code": 400007,
  "message": "name参数不能为空",
  "data": null
}

到此,我们统一参数校验也搞定了。

关于“充电桩项目”相关文章:

a6b24d7a4e3974036470a7958d757ef1.png

52a5201148a4845f883e6024a45004ed.png

分布式微服务项目实战:充电桩项目

手把手项目实战,搞完,直接写在简历上!

好了,今天就分享这么多。

如果有需要简历修改、简历优化、简历包装、面试辅导、模拟面试、技术辅导、技术支持等,欢迎加我微x(tj20120622)。

个人技术博客可刷题:http://woaijava.cc/

回复77 ,获取《面试小抄2.0版》

回复电子书,获取后端必读的200本电子书籍

文章推荐

中厂,面试就问了4道题,凉了!

分布式问题,你知道几个?

应届生,实力已超6年,太卷了!

手把手教你写简历,包装、优化!

面试不问java,问MySQL,如何破局?

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

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

相关文章

如何将一个实例的内存二进制内容读出来?

一、读取实例在内存中的字节 如下所示的PrintBytes<T>会将指定实例在内存中的字节输出到控制台上。如代码片段所示&#xff0c;我们先调用《如何计算一个实例占用多少内存&#xff1f;》中定义了SizeCalculator将承载实例内容的字节数计算出来&#xff0c;并创建对应长度…

阅读理解解题思路汇总

阅读理解解题思路汇总 一、规范解题流程&#xff1a; 1&#xff0e;读题&#xff1a; &#xff08;1&#xff09;论据→证明→论点&#xff1b; &#xff08;2&#xff09;题号&#xff1a;命题顺序与行文顺序一致&#xff1b; &#xff08;3&#xff09;题干&#xff1a;找可定…

居然生成这样的答案,AI简直离了大谱...

前文介绍了我认为目前最强的AI工具Claude&#xff1a; &#xff08;1&#xff09;目前最强的内容生成能力&#xff1b; &#xff08;2&#xff09;目前最强的个性化能力&#xff1b; &#xff08;3&#xff09;目前最强的上下文关联能力&#xff1b; &#xff08;4&#xff09;…

岩土工程中振弦采集仪的完整解决方案分析

振弦采集仪是岩土工程中用于测量地震波传播速度和土层结构信息的重要仪器。其完整解决方案包括以下几个方面&#xff1a; 1. 仪器选择&#xff1a;要选择合适的振弦采集仪&#xff0c;需要考虑测量范围、精度、可靠性、操作简便等因素。市场上常见的振弦采集仪品牌有多种&#…

chatgpt赋能python:Python并行处理介绍

Python并行处理介绍 Python是一种高级编程语言&#xff0c;通常用于数据分析&#xff0c;人工智能&#xff0c;机器学习等领域。随着数据量的增加和计算要求的增加&#xff0c;使用单个Python线程可能会变得不够快。在这种情况下&#xff0c;我们需要并行处理&#xff0c;以在…

职场上除了「躺」与「卷」,是否有第三种选择存在?

大家好&#xff0c;我是校长。 昨天在知乎上看到一个问题&#xff1a;职场上除了「躺」与「卷」&#xff0c;是否有第三种选择存在&#xff1f; 当我看到这个问题的时候&#xff0c;我想到了两本书&#xff0c;以及我看到的一些最新的观点&#xff0c;我试图结合我看到的书以及…

淘宝拍照基于端云协同的视频流实时搜索实践

本文介绍了实时视频流的主体识别场景&#xff0c;未来实时搜将会融合图搜主链路并在XR场景发力&#xff0c;未来的场景我们取名为“元视界”&#xff08;MetaSight&#xff09; 引言 很多熟悉淘宝的用户知道&#xff0c;点击首页搜索框的相机icon&#xff0c;就可以使用淘宝拍照…

助力长城汽车数据管道平台连接“数据孤岛”,加强数据一元化,Apache DolphinScheduler 的角色定位...

点击蓝字 关注我们 讲师简介 长城汽车-IDC-数据中台部-刘永飞 高级工程师 我是长城汽车 IDC-数据中台部的刘永飞&#xff0c;给大家分享一下我们自研的一个数据同步工具平台&#xff0c;以及在使用这个工具过程中遇到的问题。今天的分享主要有四个部分&#xff1a; 1. 我们自研…

语法——时态总结

一般现在时 1.概念&#xff1a;经常、反复发生的动作或行为及现在的某种状况。 2.基本结构&#xff1a;①is/am/are;②do/does否定形式&#xff1a;①am/is/are not;②此时态的谓语动词若为行为动词&#xff0c;则在其前加don‘t&#xff0c;如主语为第三人称单数&#xff0…

软考A计划-系统架构师-论文应试技巧

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

Collection接口详细介绍(上)

前言&#xff1a; 本篇文章主要讲解Java中的Collection接口以及相关实现类的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮助希望可以支持下哦~ 小威在此先感谢各位小伙伴儿了&#x1f601…

利用神经网络重建三维数字孪生

利用神经网络重建三维数字孪生 从点云重建光滑表面是创建真实世界对象和场景的数字孪生的基本步骤。 表面重建算法出现在各种应用中&#xff0c;例如工业模拟、视频游戏开发、建筑设计、医学成像和机器人技术。 神经核表面重建 (NKSR) 是新的 NVIDIA 算法&#xff0c;用于从大…

Java学习路线一条龙

作者 | 磊哥 来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09; 转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09; Java 要掌握的技术点如下&#xff1a; JavaSE&#xff08;Java Standard Edition&#xff0c;Java 基础&#xff09;计算…

常用命令之Proxmox qm命令

一、Proxmox简介 Proxmox是一款基于Debian Linux的开源虚拟化平台&#xff0c;它允许用户在单个物理主机上运行多个虚拟机&#xff0c;并提供了许多高级功能&#xff0c;如高可用性、备份和恢复、集群管理等。Proxmox支持多种虚拟化技术&#xff0c;包括KVM和LXC&#xff0c;而…

【RISC_V课程笔记】导论

目标 完成以cpu的设计&#xff0c;三级流水线结构。取指(if), 译码(id), 执行(ex)cpu中断系统的设计以cpu为核心的SOC设计&#xff0c;完成rom,ram,time的外设的设计用uvm对cpu进行验证(system verilog) 理论 数字逻辑电路 数字信号&#xff1a;用二进制表示的信号&#xf…

一起看 I/O | 隆重推出 AI 驱动的编码助手 Studio Bot

作者 / Android Studio 高级产品经理 Adarsh Fernando 我们在 2013 年 Google I/O 大会上首次宣布推出 Android Studio&#xff0c;并承诺为 Android 应用开发者提供一流的集成开发环境 (IDE)。10 年后的今天&#xff0c;我们对提高开发者工作效率的承诺仍然推动着团队开发新的…

深入理解深度学习——注意力机制(Attention Mechanism):多头注意力(Multi-head Attention)

分类目录&#xff1a;《深入理解深度学习》总目录 相关文章&#xff1a; 注意力机制&#xff08;AttentionMechanism&#xff09;&#xff1a;基础知识 注意力机制&#xff08;AttentionMechanism&#xff09;&#xff1a;注意力汇聚与Nadaraya-Watson核回归 注意力机制&#…

【LeetCode热题100】打卡第20天:合并区间不同路径

文章目录 【LeetCode热题100】打卡第20天&#xff1a;合并区间&不同路径⛅前言 合并区间&#x1f512;题目&#x1f511;题解 不同路径&#x1f512;题目&#x1f511;题解 【LeetCode热题100】打卡第20天&#xff1a;合并区间&不同路径 ⛅前言 大家好&#xff0c;我是…

MySQL中的行级锁

行级锁的类型主要有三类&#xff1a; Record Lock&#xff0c;记录锁&#xff0c;也就是仅仅把一条记录锁上&#xff1b;Gap Lock&#xff0c;间隙锁&#xff0c;锁定一个范围&#xff0c;但是不包含记录本身&#xff0c;只存在于可重复读隔离级别&#xff0c;目的是为了解决可…

前端封装一个移动端(已支持PC)滚动的组件

安装 npm install better-scroll/core --save引入 import BScroll from better-scroll/core直接上代码 Scroll.vue <template><div ref"rootRef"><slot></slot></div> </template><script> import useScroll from ./u…