提交
This commit is contained in:
214
pxdj-java/doc/基本框架设计/统一异常处理.md
Normal file
214
pxdj-java/doc/基本框架设计/统一异常处理.md
Normal file
@@ -0,0 +1,214 @@
|
||||
## 后台异常处理
|
||||
|
||||
在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`try {...} catch {...} finally {...}` 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类`RuntimeException` 与异常管理类 `DefaultExceptionHandlerConfig`。
|
||||
|
||||
我们先来看下 `RuntimeException`的代码
|
||||
|
||||
```java
|
||||
@Getter
|
||||
public class RuntimeException extends RuntimeException{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -4137688758944857209L;
|
||||
|
||||
/**
|
||||
* http状态码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
private Object object;
|
||||
|
||||
private R<?> R;
|
||||
|
||||
public RuntimeException(ResponseEnum responseEnum) {
|
||||
super(responseEnum.getMsg());
|
||||
this.code = responseEnum.value();
|
||||
}
|
||||
/**
|
||||
* @param responseEnum
|
||||
*/
|
||||
public RuntimeException(ResponseEnum responseEnum, String msg) {
|
||||
super(msg);
|
||||
this.code = responseEnum.value();
|
||||
}
|
||||
|
||||
public RuntimeException(R<?> R) {
|
||||
this.R = R;
|
||||
}
|
||||
|
||||
|
||||
public RuntimeException(String msg) {
|
||||
super(msg);
|
||||
this.code = ResponseEnum.SHOW_FAIL.value();
|
||||
}
|
||||
|
||||
public RuntimeException(String msg, Object object) {
|
||||
super(msg);
|
||||
this.code = ResponseEnum.SHOW_FAIL.value();
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
`ResponseEnum`为我们自定义的返回状态码的枚举类,定义为一个枚举类,更直观处理异常返回的状态码及异常内容,以后每增加一种异常情况,只需增加一个枚举实例即可,不用每一种异常都定义一个异常类。
|
||||
|
||||
```java
|
||||
public enum ResponseEnum {
|
||||
|
||||
/**
|
||||
* ok
|
||||
*/
|
||||
OK("00000", "ok"),
|
||||
SHOW_FAIL("A00001", ""),
|
||||
|
||||
/**
|
||||
* 用于直接显示提示用户的错误,内容由输入内容决定
|
||||
*/
|
||||
|
||||
/**
|
||||
* 用于直接显示提示系统的成功,内容由输入内容决定
|
||||
*/
|
||||
SHOW_SUCCESS("A00002", ""),
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
UNAUTHORIZED("A00004", "Unauthorized"),
|
||||
|
||||
/**
|
||||
* 服务器出了点小差
|
||||
*/
|
||||
EXCEPTION("A00005", "服务器出了点小差"),
|
||||
/**
|
||||
* 方法参数没有校验,内容由输入内容决定
|
||||
*/
|
||||
METHOD_ARGUMENT_NOT_VALID("A00014", "方法参数没有校验");
|
||||
|
||||
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
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RestControllerAdvice
|
||||
public class DefaultExceptionHandlerConfig {
|
||||
|
||||
@ExceptionHandler({ MethodArgumentNotValidException.class, BindException.class })
|
||||
public ResponseEntity<R<List<String>>> methodArgumentNotValidExceptionHandler(Exception e) {
|
||||
log.error("methodArgumentNotValidExceptionHandler", e);
|
||||
List<FieldError> fieldErrors = null;
|
||||
if (e instanceof MethodArgumentNotValidException) {
|
||||
fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
|
||||
}
|
||||
if (e instanceof BindException) {
|
||||
fieldErrors = ((BindException) e).getBindingResult().getFieldErrors();
|
||||
}
|
||||
if (fieldErrors == null) {
|
||||
return ResponseEntity.status(HttpStatus.OK)
|
||||
.body(R.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID));
|
||||
}
|
||||
|
||||
List<String> defaultMessages = new ArrayList<>(fieldErrors.size());
|
||||
for (FieldError fieldError : fieldErrors) {
|
||||
defaultMessages.add(fieldError.getField() + ":" + fieldError.getDefaultMessage());
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.OK)
|
||||
.body(R.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, defaultMessages));
|
||||
}
|
||||
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public ResponseEntity<R<?>> unauthorizedExceptionHandler(RuntimeException e){
|
||||
log.error("mall4jExceptionHandler", e);
|
||||
|
||||
R<?> R = e.getR();
|
||||
if (R!=null) {
|
||||
return ResponseEntity.status(HttpStatus.OK).body(R);
|
||||
}
|
||||
// 失败返回消息 状态码固定为直接显示消息的状态码
|
||||
return ResponseEntity.status(HttpStatus.OK).body(R.fail(e.getCode(),e.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<R<Object>> exceptionHandler(Exception e){
|
||||
log.error("exceptionHandler", e);
|
||||
return ResponseEntity.status(HttpStatus.OK).body(R.fail(ResponseEnum.EXCEPTION));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 前台异常处理
|
||||
|
||||
|
||||
|
||||
前端请求与相应做了封装,请求响应的内容会被拦截器所拦截,当后台返回给前台特定的状态码,前台将显示不同报错信息。请求响应非常常见,我们查看在`src\utils\httpRequest.js`里面的其中一段代码
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
http.interceptors.response.use(response => {
|
||||
return response
|
||||
}, error => {
|
||||
switch (error.response.status) {
|
||||
case 400:
|
||||
Message.error(error.response.data)
|
||||
break
|
||||
case 401:
|
||||
clearLoginInfo()
|
||||
router.push({ name: 'login' })
|
||||
break
|
||||
case 405:
|
||||
Message.error('http请求方式有误')
|
||||
break
|
||||
case 500:
|
||||
Message.error('服务器出了点小差,请稍后再试')
|
||||
break
|
||||
case 501:
|
||||
Message.error('服务器不支持当前请求所需要的某个功能')
|
||||
break
|
||||
}
|
||||
return Promise.reject(error)
|
||||
})
|
||||
```
|
||||
|
||||
这里将会统一拦截返回的状态码如`400`,进行错误提示。
|
||||
|
||||
|
||||
|
||||
## RESTful 风格
|
||||
|
||||
我们的上述代码使用http状态码对请求进行统一响应,其中最大的
|
||||
|
||||
RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
|
||||
|
||||
|
||||
|
||||
[RESTful概述](https://blog.igevin.info/posts/restful-architecture-in-general/)
|
||||
Reference in New Issue
Block a user