SpringBoot实现统一异常处理

简介: 大家在使用SpringBoot开发项目的时候肯定都需要处理异常吧,没有处理异常那么异常信息直接显示给用户这是非常不雅观的,同时还可能造成用户误会,那么今天我们就来简单的写一下如何在SpringBoot项目中实现统一的异常处理。
大家在使用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("删除成功");
}
至此,关于统一异常的处理就已经完成了,这时候系统再出现异常,就不会说是直接抛出异常堆栈信息,而是一个非常优雅的异常提示信息。当然这只是一个小小的例子,具体的异常处理方式需要根据实际情况进行调整。
目录
相关文章
|
缓存 前端开发 Java
SpringBoot&SpringMVC统一异常处理之RestControllerAdvice
SpringBoot&SpringMVC统一异常处理之RestControllerAdvice
137 0
|
7月前
|
SQL 前端开发 Java
SpringBoot 拦截器 统一结果返回 统一异常处理
SpringBoot 拦截器 统一结果返回 统一异常处理
375 0
|
Java
SpringBoot通用异常处理
通用异常返回一般用在该异常服务器无法处理的时候,进行消息的返回。所以返回代码只有 500。
101 0
|
存储 运维 Java
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder) 1
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder)
|
人工智能 Java
SpringBoot实战(十):统一异常处理
SpringBoot实战(十):统一异常处理
|
JSON 前端开发 Java
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder) 2
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder)
|
Java 数据安全/隐私保护
Springboot统一异常处理
Springboot统一异常处理
|
消息中间件 JSON JavaScript
SpringMVC 统一异常处理实战
SpringMVC 统一异常处理实战
|
JSON Java 数据格式
对Springboot项目进行统一异常处理
对Springboot项目进行统一异常处理
对Springboot项目进行统一异常处理
|
Java Spring
【笑小枫的SpringBoot系列】【五】SpringBoot返回统一异常处理
【笑小枫的SpringBoot系列】【五】SpringBoot返回统一异常处理
83 0