spring boot 全局错误异常处理
1. 背景
在开发一个前后端分离的项目时,难免会遇到各种各样的业务逻辑错误,例如某个接口,其中可能有十种情况会影响业务的清空,如果是一个对用户比较友好的项目的话,对于这10种情况都会做对应的错误处理,能够让用户清楚业务不成功的具体原因。向阿里云,腾讯云等提供的sdk,都会定义自己的错误异常文件,进而将异常信息抛出来,让用户清楚自己错误的原因。
一般我们使用抛出异常或者通过修改自定义的返回体,来将错误信息给前端进行返回。在抛出异常的时候,我们会希望能够根据该异常,返回一个统一数据结构,这样能够方便前端进行处理。所以我们需要想办法通过controller层对异常进行处理,然后按照预定的格式内容返回给前端json字符串。
因为有时候我们在完成一个接口的时候,有些情况下并不是直接发生了错误,可能是在经过了几个service的时候,出现了错误,这个时候因为每一层service都会定义个一个返回值类型,所以很难去将我们的错误类型给抛出来,如果通过写上一堆if else来判断函数执行有没有正常执行的话,代码可读性以及可扩展性太差了。
这个时候如果将异常抛出,然后最上层统一进行处理,就能够避免这种问题
类似这种,会规定错误码以及对应的错误信息,方便用户排查。
2.数据结构
在进行返回数据的时候,考虑到兼容性以及用户友好,设计了一下的数据结构
public class Result<T> implements Serializable {
private String errCode = "0";
private T data;
private String errorMsg;
private Boolean status;
}
其中errCode采用String类型,默认为0,即0为业务处理成功。
如果业务正常处理成功,则将status置为true,并且将数据放在data中进行返回。
3. 错误常量定义
因为考虑到会有较多的错误类型,为了统一进行管理,我使用了一个枚举类来盛放所有的错误类型,这样对同一种类型的异常,我就能够通过使用一个异常类,然后通过传入不同的枚举类型来进行处理。个人觉得这样能够对效率有所提升
public enum ErrorConstant {
RESOURCE_ACCOUNT_EXIST("10001", "资源账户已经存在"),
RESOURCE_ACCOUNT_PRODUCT_EXIST("10011", "资源产品已经关联");
private String value;
private String desc;
ErrorConstant(String value, String desc) {
this.value = value;
this.desc = desc;
}
public String getValue() {
return value;
}
public String getDesc() {
return desc;
}
}
4. 编写自定义异常类
这里我们需要写一个我们自己的异常类,之后出现预料中错误时,可以直接抛出该异常类
public class BaseException extends Exception {
private ErrorConstant errorConstant;
//在新建BaseException对象的时候,传入一个错误枚举,这样方便在处理异常的时候使用
public BaseException(ErrorConstant errorConstant) {
this.errorConstant = errorConstant;
}
}
所有的自定义异常类都要继承Exception或者已经继承Exception的子类。
5. 编写异常处理类
这里我们主要会用到三个注解
@ControllerAdvice:该注解作用于整个spring工程,定义了一个会对全局的异常都进行捕获
@ResponseBody:定义返回JSON给前端
@ExceptionHandler :捕获指定的Exception,然后进行处理
@ControllerAdvice
@ResponseBody
@Slf4j
public class ResponseExceptionHandler {
@ExceptionHandler(BaseException.class)
Result handleCustomException(BaseException e) {
return new Result(e.getErrorConstant());
}
}
这里我只对BaseException这一种类型的异常进行处理,也就是说所有抛出的BaseException,都会在这里进行处理,然后返回给前端。
6. 异常抛出
这里我在处理资源账户的时候,针对存在的账户,抛出一个异常,错误信息为资源账户已存在。
public Boolean createResourceAccount(ResourceAccountRequest resourceAccountRequest) throws Exception {
if (resourceAccountRepository.findByResourceId(resourceAccountRequest.getResourceId())
!= null) {
throw new BaseException(ErrorConstant.RESOURCE_ACCOUNT_EXIST);
}
}
我只需要每一层都把异常抛出去,然后由最上层的ExceptionHandler来进行处理就好了。
通过将异常由最上层统一处理,就能够避免在业务处理过程中,各种情况的判断,能够大幅度的提高开发效率以及代码可读性。