216 lines
5.4 KiB
Markdown
216 lines
5.4 KiB
Markdown
## 统一异常处理
|
|
|
|
### 后端异常处理
|
|
|
|
在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`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<ServerResponseEntity<Object>> 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`,进行错误提示。
|