首页 > 其他分享 >SpringBoot实现统一异常处理

SpringBoot实现统一异常处理

时间:2023-08-22 11:07:36浏览次数:39  
标签:SpringBoot class ResultEnum msg import 异常 public 统一

大家在使用SpringBoot开发项目的时候肯定都需要处理异常吧,没有处理异常那么异常信息直接显示给用户这是非常不雅观的,同时还可能造成用户误会,那么今天我们就来简单的写一下如何在SpringBoot项目中实现统一的异常处理。

1.自定义异常类

我们先定义一个自定义业务异常类,这个异常类继承了 RuntimeException,并添加了一个 code 属性,用于标识错误码,以及一个 msg 属性,用于标识错误信息。

package cn.youhaveme.exception;

import cn.youhaveme.entity.result.ResultEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class BusinessException extends RuntimeException {
    private Integer code;
    private String msg;

    public BusinessException(String msg) {
        this.code = ResultEnum.BUSINESS_EXCEPTION.getCode();
        this.msg = msg;
    }
}

2.定义全局异常处理器

接下来,我们需要定义一个全局异常处理器,这个主要是用于捕获Controller的异常并处理异常。在 SpringBoot 中,我们可以使用@RestControllerAdvice 注解来定义一个异常处理器,这个异常处理器可以处理所有的异常,在每个不同的处理器中,还可以通过@ExceptionHandler(异常.class)来区分不同的异常,以此来做出不同的响应信息。

package cn.youhaveme.exception;

import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.youhaveme.entity.result.Result;
import cn.youhaveme.entity.result.ResultEnum;
import cn.youhaveme.entity.result.ResultJson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;
import java.util.stream.Collectors;

@RestControllerAdvice
@Slf4j
public class GlobalException {

    // 拦截:未登录异常
    @ExceptionHandler(NotLoginException.class)
    public Result<String> handlerException(NotLoginException e) {
        log.error("登录未认证异常", e);
        String msg;
        switch (e.getType()) {
            case NotLoginException.NOT_TOKEN:
                msg = "账号已注销,请重新登录";
                break;
            case NotLoginException.INVALID_TOKEN:
                msg = NotLoginException.INVALID_TOKEN_MESSAGE;
                break;
            case NotLoginException.TOKEN_TIMEOUT:
                msg = "Token已过期,请重新登录";
                break;
            case NotLoginException.BE_REPLACED:
                msg = "账号被顶强制下线";
                break;
            case NotLoginException.KICK_OUT:
                msg = "账号被强制踢出,请重新登录";
                break;
            default:
                msg = "当前会话未登录";
        }
        return ResultJson.goErrorResponse(ResultEnum.UNAUTHORIZED.getCode(), msg);
    }

    // 拦截:缺少权限异常
    @ExceptionHandler(NotPermissionException.class)
    public Result<String> handlerException(NotPermissionException e) {
        log.error("缺少权限异常", e);
        return ResultJson.goErrorResponse(ResultEnum.FORBIDDEN.getCode(), "缺少权限:" + e.getPermission());
    }

    // 拦截:缺少角色异常
    @ExceptionHandler(NotRoleException.class)
    public Result<String> handlerException(NotRoleException e) {
        log.error("缺少角色异常", e);
        return ResultJson.goErrorResponse(ResultEnum.FORBIDDEN.getCode(), "缺少角色:" + e.getRole());
    }

    // 拦截:服务封禁异常
    @ExceptionHandler(DisableServiceException.class)
    public Result<String> handlerException(DisableServiceException e) {
        log.error("服务封禁异常", e);
        return ResultJson.goErrorResponse(ResultEnum.SERVICE_EXCEPTION.getCode(), "当前账号 " + e.getService() + " 服务已被封禁 (level=" + e.getLevel() + "):" + e.getDisableTime() + "秒后解封");
    }

    // 拦截:参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<String> handlerException(MethodArgumentNotValidException e) {
        log.error("参数校验异常", e);
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
        return ResultJson.goErrorResponse(ResultEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION.getCode(), message);
    }

    // 拦截:业务逻辑异常
    @ExceptionHandler(BusinessException.class)
    public Result<String> handlerException(BusinessException e) {
        log.error("业务逻辑异常", e);
        return ResultJson.goErrorResponse(ResultEnum.BUSINESS_EXCEPTION.getCode(), e.getMsg());
    }

    // 拦截:系统异常(兜底异常)
    @ExceptionHandler(Exception.class)
    public Result<String> handlerException(Exception e) {
        log.error("系统异常", e);
        return ResultJson.goErrorResponse(ResultEnum.SYSTEM_EXCEPTION.getCode(), ResultEnum.SYSTEM_EXCEPTION.getMessage());
    }

}
3.统一的接口返回

这个统一的接口返回也是非常有必要的,毕竟一个统一的接口,不仅仅可以处理成功信息,这部分的异常信息也同样可以处理,那么我之前写过一篇有关SpringBoot统一返回的文章,有兴趣的可以点击去翻阅一下,这里就不重复介绍了。

4.自定义异常抛出

在业务代码中,如果发生了自定义异常,或者有需要我们自己处理的一些东西,我们就需要选择性的将异常抛出,这样才会被全局异常处理器所拦截,并作出相应的处理逻辑。

@DeleteMapping("/delete")
public Result<String> delete(@Validated({ValidGroup.Delete.class}) @RequestBody User user) {
    log.info("删除用户:{}", user);
    User dbUser = userService.getUserByUserName(user.getUserName());
    if (BeanUtil.isEmpty(dbUser)) {
        throw new BusinessException("该用户不存在,无法删除");
    }
    boolean removeResult = userService.removeById(dbUser.getId());
    if (!removeResult) {
        throw new BusinessException("用户删除失败");
    }
    return ResultJson.goSuccessResponse("删除成功");
}

至此,关于统一异常的处理就已经完成了,这时候系统再出现异常,就不会说是直接抛出异常堆栈信息,而是一个非常优雅的异常提示信息。当然这只是一个小小的例子,具体的异常处理方式需要根据实际情况进行调整。

标签:SpringBoot,class,ResultEnum,msg,import,异常,public,统一
From: https://blog.51cto.com/u_16167640/7186793

相关文章

  • 基于springboot课程答疑系统
    随着信息互联网信息的飞速发展,无纸化作业变成了一种趋势,针对这个问题开发一个专门适应师生交流形式的网站。本文介绍了课程答疑系统的开发全过程。通过分析企业对于课程答疑系统的需求,创建了一个计算机管理课程答疑系统的方案。文章介绍了课程答疑系统的系统分析部分,包括可行性分析......
  • 爬虫异常处理:异常捕获与容错机制设计
    作为一名专业的爬虫程序员,每天使用爬虫IP面对各种异常情况是我们每天都会遇到的事情。在爬取数据的过程中,我们经常会遇到网络错误、页面结构变化、被反爬虫机制拦截等问题。在这篇文章中,我将和大家分享一些关于如何处理爬虫异常情况的经验和技巧。通过异常捕获和容错机制的设计,我们......
  • 基于springboot师生共评的作业管理系统设计与实现
    随着信息互联网信息的飞速发展,无纸化作业变成了一种趋势,针对这个问题开发一个专门适应师生作业交流形式的网站。本文介绍了师生共评的作业管理系统的开发全过程。通过分析企业对于师生共评的作业管理系统的需求,创建了一个计算机管理师生共评的作业管理系统的方案。文章介绍了师生共......
  • springSecurity异常提示国际化
    1:获取国际化文件在一个jar包里,可以先下载jar包,然后再里面找到中文的那个文件<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId><version>3.2.0.RELEASE</vers......
  • SpringBoot内嵌Tomcat连接池分析
    目录1Tomcat连接池1.1简介1.2架构图1.2.1JDK线程池架构图1.2.2Tomcat线程架构1.3核心参数1.3.1AcceptCount1.3.2MaxConnections1.3.3MinSpareThread/MaxThread1.3.4MaxKeepAliveRequests1.3.5ConnectionTimeout1.3.6KeepAliveTimeout1.4核心内部线程1.4.1Acceptor1.......
  • SpringBoot 测试实践 - 2:单元测试与集成测试
    单元测试vs.集成测试只编写单测,无法测试方法之间的集成情况,而且某些需求可能会修改多个方法,这可能会影响方法对应的单测,涉及到大量的相关单测的修改,这样的维护成本很高可以把重心放在完善集成测试上,专注从外部判断程序是否符合预期。对于一些非常重要的方法,增加单元测试可以减......
  • 搭建SpringCloudAlibaba工程_基于SpringBoot3.+
    打开IDEA新建工程 删除所有文件,仅保留pom.xml,并做出如下修改,可直接复制粘贴,切记调整你为自己项目的groupId和artifactId<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/X......
  • SpringBoot复习:(36)国际化
    一、Resources目录下建立一个目录(比如international)来存储资源文件message.properties空的,但不能没有message_zh_CN.propertieshello=您好message_en_us.propertieshello=helloworld二、自动配置类MessageSourceAutoConfiguration常量MESSAGE_SOURCE_BEAN_NAME为messageSourc......
  • SpringBoot复习:(40)@EnableConofigurationProperties注解的用法
    一、配置文件:server.port=9123二、配置类:packagecn.edu.tju.config;importcom.mysql.fabric.Server;importorg.springframework.boot.autoconfigure.web.ServerProperties;importorg.springframework.boot.context.properties.EnableConfigurationProperties;importorg.......
  • SpringBoot复习:(44)MyBatisAutoConfiguration
    可以看到MyBatisAutoConfiguration引入了MyBatisProperties这个属性:MyBatisAutoConfiguration中配置了一个SqlSessionFactoryBean,代码如下:可以配置mybatis-config.xml,需要配置文件里指定:mybatis.config-locatinotallow=classpath:/mybatis-config.xml同样可配置MyBatis的xml......