前言
这篇文章主要讲解了,基于SpringBoot搭建的前后端分离项目中,如何全局捕获异常,并封装统一的返回值。
一、编写异常信息接口
首先编写一个接口,包含2个抽象方法,分别是获取错误码的方法和获取错误描述的方法。
public interface BaseErrorInfoInterface {
/**
* 错误码
*/
Integer getResultCode();
/**
* 错误描述
*/
String getResultMsg();
}
二、编写错误码枚举类
自定义一个返回值错误码枚举类,这个枚举类实现上面的BaseErrorInfoInterface接口,并重写它的2个抽象方法。 这个枚举类包含3个成员变量,分别为success,code和message,success是boolean类型的,code用于返回错误码,message返回错误信息。随后我们在枚举类中定义了一些常用的返回值,这些返回值在后面的代码中会用到。
@Getter
public enum ResultCodeEnum implements BaseErrorInfoInterface {
SUCCESS(true, 200, "成功"),
NULL_POINTER(false, 500, "空指针异常"),
UN_KNOW(false, 500, "服务器未知内部错误"),
COMMON_FAIL(false, 5000, "失败");
private Boolean success;
private Integer code;
private String message;
private ResultCodeEnum(Boolean success, Integer code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
//重写父接口的抽象方法,返回错误码
@Override
public Integer getResultCode() {
return code;
}
//重写父接口的抽象方法,返回错误信息
@Override
public String getResultMsg() {
return message;
}
}
错误码枚举类写完后,我们需要封装一个返回值的工具类,继续往下看吧。
三、封装返回值工具类
我们姑且把这个返回值工具类命名为Result,其中包含4个成员变量,success、code、message和data。其中success、code和message的数据来自上面创建的枚举类,而data则是HashMap类型的,它用于存放我们要返回给前端的数据。
@Data
public class Result {
private Boolean success;
private Integer code;
private String message;
private Map<String, Object> data = new HashMap<>();
//请求成功时的返回值
public static Result ok() {
Result r = new Result();
r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
r.setCode(ResultCodeEnum.SUCCESS.getCode());
r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
return r;
}
//请求失败时的返回值
public static Result fail() {
Result r = new Result();
r.setSuccess(ResultCodeEnum.COMMON_FAIL.getSuccess());
r.setCode(ResultCodeEnum.COMMON_FAIL.getCode());
r.setMessage(ResultCodeEnum.COMMON_FAIL.getMessage());
return r;
}
public static Result setResult(ResultCodeEnum resultCodeEnum) {
Result r = new Result();
r.setSuccess(resultCodeEnum.getSuccess());
r.setCode(resultCodeEnum.getCode());
r.setMessage(resultCodeEnum.getMessage());
return r;
}
public Result success(Boolean success) {
this.setSuccess(success);
return this;
}
public Result message(String message) {
this.setMessage(message);
return this;
}
public Result code(Integer code) {
this.setCode(code);
return this;
}
public Result data(String key, Object value) {
this.data.put(key, value);
return this;
}
public Result data(Map<String, Object> map) {
this.setData(map);
return this;
}
public Map<String, Object> toMap() {
return this.data;
}
/**
* @作者 yangs
* @日期 2022/1/20
* @描述 异常时调用
*/
public static Result error(BaseErrorInfoInterface errorInfo) {
Result r = new Result();
r.setSuccess(false);
r.setCode(errorInfo.getResultCode());
r.setMessage(errorInfo.getResultMsg());
return r;
}
/**
* @作者 yangs
* @日期 2022/1/20
* @描述 异常时调用
*/
public static Result error(Integer code, String message) {
Result r = new Result();
r.setSuccess(false);
r.setCode(code);
r.setMessage(message);
return r;
}
/**
* @作者 yangs
* @日期 2022/1/20
* @描述 异常时调用
*/
public static Result error(String message) {
Result r = new Result();
r.setSuccess(false);
r.setCode(500);
r.setMessage(message);
return r;
}
private Result() {
}
public Result(Boolean success, Integer code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
/**
* @作者 yangs
* @日期 2022/1/20
* @描述 基于全局异常处理类封装的返回值
*/
private Result(BaseErrorInfoInterface errorInfo) {
this.code = errorInfo.getResultCode();
this.message = errorInfo.getResultMsg();
}
}
到目前为止,我们已经完成了返回值部分的代码,接下来我们需要自定义一个异常处理类。
四、配置全局异常处理
自定义异常处理类,并继承RuntimeException类,我们可以把自定义的异常类命名为BizException,当我们业务出现错误,程序员可以主动throw 一个BizException,具体用法我们会在后面写一个Controller演示。
/**
* @作者 yangs
* @日期 2022/1/20
* @描述 自定义异常处理类
*/
public class BizException extends RuntimeException {
/**
* 错误码
*/
protected Integer errorCode;
/**
* 错误信息
*/
protected String errorMsg;
public BizException() {
super();
}
public BizException(BaseErrorInfoInterface errorInfoInterface) {
super(String.valueOf(errorInfoInterface.getResultCode()));
this.errorCode = errorInfoInterface.getResultCode();
this.errorMsg = errorInfoInterface.getResultMsg();
}
public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
super(String.valueOf(errorInfoInterface.getResultCode()), cause);
this.errorCode = errorInfoInterface.getResultCode();
this.errorMsg = errorInfoInterface.getResultMsg();
}
public BizException(String errorMsg) {
super(errorMsg);
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg) {
super(String.valueOf(errorCode));
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg, Throwable cause) {
super(String.valueOf(errorCode), cause);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getMessage() {
return errorMsg;
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
五、配置全局异常处理
SpringBoot提供了全局异常处理功能,需要借助@ControllerAdvice和@ExceptionHandler注解。@ControllerAdvice用于开启全局异常,它是加在类上面的,而@ExceptionHandler加在方法上,它表示具体捕获哪一种异常。
/**
* @作者 yangs
* @日期 2022/1/20
* @描述 全局异常处理
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理自定义的业务异常,程序员主动抛出BizException异常会被这个方法捕获
*/
@ExceptionHandler(value = BizException.class)
@ResponseBody
public Result bizExceptionHandler(HttpServletRequest req, BizException e) {
log.error("发生业务异常!原因是:{}", e.getErrorMsg());
return Result.error(e.getErrorCode(), e.getErrorMsg());
}
/**
* 处理空指针的异常(空指针异常会被这个方法捕获)
*/
@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, NullPointerException e) {
log.error("发生空指针异常!原因是:", e);
return Result.error(ResultCodeEnum.NULL_POINTER);
}
/**
* 处理其他异常
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, Exception e) {
log.error("未知异常!原因是:", e);
return Result.error(ResultCodeEnum.UN_KNOW);
}
}
到这里我们已经完成了Springboot捕获全局异常并以固定格式返回,接下来我们写个Controller测试一下。
六、测试
随便写个controller测试一下。
@RestController
@RequestMapping("/test")
public class TestController {
//返回正确返回值
@GetMapping("/testResult")
public Result testResult() {
//此处直接调用Result的静态方法,返回数据
return Result.ok();
}
//测试空指针异常
@GetMapping("/testNullPoint")
public Result testNullPoint() {
//故意抛出一个空指针,我们发现会被GlobalExceptionHandler捕获。
NldDoctor doctor = null;
String name = doctor.getName();
return Result.ok();
}
//主动抛出自定义的异常
@GetMapping("/testOtherException")
public Result testOtherException() {
//故意抛出异常
int a = 10 / 0;
return Result.ok();
}
//主动抛出自定义的异常
@GetMapping("/throwBizException")
public Result throwBizException() {
//程序员根据业务需要,主要抛出BizException异常
String name = "jack";
if (name.equals("jack")) {
throw new BizException(500, "出错了");
}
return Result.ok();
}
}
调用Controller接口,我们会发现Controller控制器中,产生的异常都会被GlobalExceptionHandler类捕获,当没有异常时,我们可以调用Result工具类的各种静态方法,返回给前端期望的数据。以上便是本篇文章的全部内容,觉得有用的同学记得点赞哦!!!