《springboot实战》第六章 实现自定义全局异常处理

系列文章目录

第一章 SpringBoot起步
第二章 springboot 配置文件、多环境配置、运行优先级
第三章 springboot 统一日志
第四章 SpringBoot加载静态文件资源
第五章 springboot 拦截器
第六章 实现自定义全局异常处理
第七章 springboot 数据库应用
第八章 springboot 整合Druid
第九章 springboot 整合MyBatis - xml、注解篇
第十一章 整合Mybatis plus
第十二章 SpringBoot整合swagger-bootstrap-ui



在这里插入图片描述


前言

springboot实现自定义全局异常处理,以及统一返回数据。

1、分析

首先,实现全局异常的流程
在这里插入图片描述
从图中可以看到,实现全局异常会需要这样几个类:

  • 自定义异常接口类
  • 自定义异常枚举类
  • 自定义异常类
  • 自定义异常处理类
  • 自定义全局响应类

2、创建所需类

2.1、为了代码解耦,创建一个接口类出来,定义自定义接口所需要的方法

/**
 * Http状态信息接口
 */
public interface HttpStatusInfoInterface {

    int getCode();

    String getMessage();

}

2.2、定义一个枚举类,实现上述接口,重写上述接口的两个方法来操作这个枚举类内部的各个具体枚举值

后续方便管理所有错误枚举的错误信息以及code码,通过构造方法传入code值和message或者直接传入一个枚举值都行

/**
 * Http状态码
 */
public enum HttpStatusEnum implements HttpStatusInfoInterface{

    //定义状态枚举值
    SUCCESS(200 , "成功!"),
    BODY_NOT_MATCH(400 , "数据格式不匹配!"),
    NOT_FOUND(404 , "访问资源不存在!"),
    INTERNAM_SERVER_ERROR(500 , "服务器内部错误!"),
    SERVER_BUSY(503 , "服务器正忙,请稍后再试!"),
    REQUEST_METHOD_SUPPORT_ERROR(10001 , "当前请求方法不支持!"),
    REQUEST_DATA_NULL(10002 , "当前请求参数为空!"),
    USER_NOT_EXISTS(10003 , "该用户不存在!"),
    USER_INVALID(10004 , "当前登录信息已生效,请重新登录!"),
    PASSWORD_ERROR(10005 , "密码错误!"),
    USER_NAME_LOCK(10006 , "该账号已被锁定!");

    //状态码
    private int code;

    //提示信息
    private String message;

    //构造方法
    HttpStatusEnum(int code , String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public int getCode() {
        return this.code;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

}

2.3、自定义一个异常类

就像空指针异常类、IO流异常类一样。此处自定义的异常类属于异常类,所有肯定是要继承一个异常类的,此处需要继承RuntimeException,原因如下:
RuntimeException相比Exception来讲,他是在程序运行时才会爆出异常,在编译时是不会出现异常的,这就表示,如果你throw了一个RuntimeException,不需要做额外操作;而throw一个Exception,程序会要求你try-catch,否则你根本启动不了程序,程序会提示(必须对其进行捕获或声明以便抛出)

import com.xxxx.springbootmybatis.common.HttpStatusEnum;
import lombok.Data;

@Data
public class HttpException extends RuntimeException{

    //错误码
    private int code;

    //错误信息
    private String message;

    // 默认构造函数
    public HttpException() {
        super();
    }

    public HttpException(HttpStatusEnum httpStatusEnum) {
        super(String.valueOf(httpStatusEnum.getCode()));
        this.code = httpStatusEnum.getCode();
        this.message = httpStatusEnum.getMessage();
    }

}

2.4、封装统一返回类

封装返回值类BaseResponse类和RespGenerator类都是属于规范方法返回值结构体的类,也有利于一致化后端所有接口的返回结构,方便前端读取所需要的数据。
HttpResult类:规定返回值结构。
HttpResultGenerator类:将逻辑处理后的数据包装转换成HttpResult类进行返回给前端。

import lombok.Data;

/**
 * http 统一返回类
 * @param <T>
 */
@Data
public class HttpResult<T> {

    private Integer code;

    private String message;

    private T data;

    //构造方法
    public HttpResult(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    //默认构造函数
    public HttpResult() {
        super();
    }
}

public class HttpResultGenerator {

    //正常返回时调用方法
    public static HttpResult success(Object data) {
        return new HttpResult(HttpStatusEnum.SUCCESS.getCode() , "接口调用成功!" , data);
    }


    //失败时调用方法(入参是异常枚举)
    public static HttpResult fail(HttpStatusEnum httpStatusEnum) {
        return new HttpResult(httpStatusEnum.getCode() , httpStatusEnum.getMessage() , null);
    }

    //失败时调用方法(提供给GlobalExceptionHandler类使用)
    public static HttpResult fail(int code ,  String message) {
        return new HttpResult(code , message , null);
    }

}

2.5、自定义异常处理类

@RestControllerAdvice注解是@ResponseBody和@ControllerAdvice的组合。

@ResponseBody注解:通常用来将java对象转成JSON对象,返回给前端JSON数据。
@ControllerAdvice注解:结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。
@ExceptionHandler注解统一处理某一类异常,从而能够减少代码重复率和复杂度,value值为什么异常类型,就处理什么异常类型的逻辑。

import lombok.extern.slf4j.Slf4j;
import com.xxxx.springbootmybatis.common.HttpResult;
import com.xxxx.springbootmybatis.common.HttpResultGenerator;
import com.xxxx.springbootmybatis.common.HttpStatusEnum;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 自定义异常处理类
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
	
    //处理自定义异常
    @ExceptionHandler(value = HttpException.class)
    public HttpResult<Object> baseExceptionHandler(HttpException e) {
        log.error("发生业务异常!原因是:{}" , e.getMessage());
        return HttpResultGenerator.fail(e.getCode() , e.getMessage());
    }

    //处理空指针异常
    @ExceptionHandler(value = NullPointerException.class)
    public HttpResult<Object> exceptionHandler(Exception e) {
        log.error("发生异常!原因是:{}" , e);
        return HttpResultGenerator.fail(HttpStatusEnum.INTERNAM_SERVER_ERROR);
    }

}

3、测试

新建一个测试controller。

3.1、测试自定义异常全局处理效果

@Slf4j
@RestController
@CrossOrigin("*")
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/loginTest")
    public HttpResult loginTest(@RequestParam(value="name" ,required=false) String userName,
                                @RequestParam(value="pwd" ) String password ) {
        // URL: http://127.0.0.1/user/loginone?userName=zs&pwd=123
        log.info("userName:{} , password:{}" , userName , password);
        if(StringUtils.isEmpty(userName)) {
            throw new HttpException(HttpStatusEnum.USER_NOT_EXISTS);
        } else {
            return HttpResultGenerator.success("登录校验成功");
        }
    }
}

3.2、用postman测试,若username没有传值,会抛出自定义异常

在这里插入图片描述

3.3、控制台结果

2023-04-19 17:47:51.411 |-INFO  [http-nio-80-exec-3] com.hqyj.springbootmybatis.controller.UserController [54] -| userName: , password:123
2023-04-19 17:47:51.411 |-ERROR [http-nio-80-exec-3] com.hqyj.springbootmybatis.common.exception.GlobalExceptionHandler [21] -| 发生业务异常!原因是:该用户不存在!

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青花科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值