## 统一异常处理 ### 后端异常处理 在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`try {...} catch {...} finally {...}` 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类`LuckException` 与异常管理类 `DefaultExceptionHandlerConfig`。 我们先来看下 `LuckException`的代码 ```java public class LuckException extends RuntimeException { private static final long serialVersionUID = 1L; private Object object; /** * 响应状态码枚举 */ private ResponseEnum responseEnum; public LuckException(String msg) { super(msg); } public LuckException(String msg, Object object) { super(msg); this.object = object; } public LuckException(String msg, Throwable cause) { super(msg, cause); } public LuckException(ResponseEnum responseEnum) { super(responseEnum.getMsg()); this.responseEnum = responseEnum; } public LuckException(ResponseEnum responseEnum,Object object) { super(responseEnum.getMsg()); this.responseEnum = responseEnum; this.object = object; } public Object getObject() { return object; } public ResponseEnum getResponseEnum() { return responseEnum; } } ``` `ResponseEnum`为我们自定义的返回状态码的枚举类,定义为一个枚举类,更直观处理异常返回的状态码及异常内容,以后每增加一种异常情况,只需增加一个枚举实例即可,不用每一种异常都定义一个异常类。 ```java public enum ResponseEnum { /** * ok */ OK("00000", "ok"), /** * 用于直接显示提示用户的错误,内容由输入内容决定 */ SHOW_FAIL("A00001", ""), /** * 方法参数没有校验,内容由输入内容决定 */ METHOD_ARGUMENT_NOT_VALID("A00002", ""), /** * 无法读取获取请求参数 */ HTTP_MESSAGE_NOT_READABLE("A00003", "请求参数格式有误"), /** * 未授权 */ UNAUTHORIZED("A00004", "Unauthorized"), /** * 服务器出了点小差 */ EXCEPTION("A00005", "服务器出了点小差"); private final String code; private final String msg; public String value() { return code; } public String getMsg() { return msg; } ResponseEnum(String code, String msg) { this.code = code; this.msg = msg; } @Override public String toString() { return "ResponseEnum{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + "} " + super.toString(); } } ``` 再来看看 `DefaultExceptionHandlerConfig`类 ```java @RestController @RestControllerAdvice public class DefaultExceptionHandlerConfig { private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionHandlerConfig.class); @ExceptionHandler(LuckException.class) public ResponseEntity> luckExceptionHandler(LuckException e) { logger.error("mall4cloudExceptionHandler", e); ResponseEnum responseEnum = e.getResponseEnum(); // 失败返回失败消息 + 状态码 if (responseEnum != null) { return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(responseEnum, e.getObject())); } // 失败返回消息 状态码固定为直接显示消息的状态码 return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.showFailMsg(e.getMessage())); } } ``` --- ### 前端异常处理 前端请求与相应做了封装,请求响应的内容会被拦截器所拦截,当后台返回给前台特定的状态码,前台将显示不同报错信息。请求响应非常常见,我们查看在`src\utils\request.js`里面的其中一段代码 ```javascript service.interceptors.response.use( response => { const res = response.data if (res.code === '00000') { return res.data } // A00001 用于直接显示提示用户的错误,内容由输入内容决定 // A00003 无法读取获取请求参数 if (res.code === 'A00001' || res.code === 'A00003' || res.code === 'A00005') { Message({ message: res.msg || 'Error', type: 'error', duration: 1.5 * 1000 }) return Promise.reject(res) } // A00002 方法参数没有校验,内容由输入内容决定 if (res.code === 'A00002') { if (res.data && res.data.length) { res.data.forEach(errorMsg => { Message({ message: errorMsg || 'Error', type: 'error', duration: 1.5 * 1000 }) }) } else { Message({ message: res.msg || 'Error', type: 'error', duration: 1.5 * 1000 }) } return Promise.reject() } // A00004 未授权 if (res.code === 'A00004') { // to re-login MessageBox.confirm('您已注销,您可以取消停留在该页上,或重新登录', '确认注销', { confirmButtonText: '重新登陆', cancelButtonText: '取消', type: 'warning' }).then(() => { store.dispatch('user/resetToken').then(() => { location.reload() }) }) return Promise.reject() } return Promise.reject(res) }, error => { console.log('err' + error) // for debug Message({ message: error.message, type: 'error', duration: 1.5 * 1000 }) return Promise.reject(error) } ) ``` 这里将会统一拦截返回的状态码如`A00001`,进行错误提示。