1. 项目环境
- IDEA 2020.1.4
- Maven 3.6
- JDK 1.8
- SpringBoot 2.x
项目文件在GitHub(欢迎star⭐):
https://github.com/Gang-bb/Gangbb-SpringBoot
注意:此节内容包含SpringBoot系列-- SpringBoot统一对象返回小节内容
SpringBoot系列-- SpringBoot统一对象返回
如有疑问或是建议,欢迎评论区留言或者QQ:949526365
2. 对Java异常的一些理解
-
CheckedException: 必须在编码时进行处理,不处理程序是无法通过编译的。可以向上throws抛出或者就地try/catch解决,比如记日志。(程序员可以处理)
举例:1. B.C() B调用C方法,但C没有定义。 2. 读取文件,文件不存在。此时可能文件路径不对,改对就处理完成了
-
RunTimeException:运行时异常,可以通过编译,不强制要求要处理。
举例:比如用户输入2000 id号。数据库找不到,就是一种RunTimeException
3. 处理异常
目标是每次返回的异常都是下面这种格式:
![image-20210123095521770](https://i.loli.net/2021/01/23/woShIa4LNupbceY.png)
3.1 定义统一异常类
public class ApiException extends RuntimeException{
private final Integer code;
private final String message;
public ApiException(Integer code, String message) {
this.code = code;
this.message = message;
}
public ApiException(ApiExceptionEnum exceptionEnum) {
this(exceptionEnum.getCode(), exceptionEnum.getMsg());
}
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
3.2 定义全局异常拦截
import com.gangbb.gangbbspringbootexception.common.ApiRestResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import javax.servlet.ServletException;
import java.util.ArrayList;
import java.util.List;
/**
* @author : Gangbb
* @ClassName : GlobalExceptionHandler
* @Description :
* @Date : 2021/1/23 10:04
*/
//拦截异常注解 @ControllerAdvice表明这时一个异常处理类
@ControllerAdvice
public class GlobalExceptionHandler {
private final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
public Object handleHttpException(ServletException e) {
log.error("ServletException: ", e);
return ApiRestResponse.error(ApiExceptionEnum.HTTP_ERROR.getCode(), e.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseBody
public Object handleException(Exception e) {
log.error("Default Exception: ", e);
return ApiRestResponse.error(ApiExceptionEnum.SYSTEM_ERROR);
}
@ExceptionHandler(ApiException.class)
@ResponseBody
public Object handleFirstMallException(ApiException e) {
log.error("ApiException: ", e);
return ApiRestResponse.error(e.getCode(), e.getMessage());
}
/**
* 参数校验异常处理
* @param e
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ApiRestResponse handleMethodArgumentNotValidException(
MethodArgumentNotValidException e) {
log.error("MethodArgumentNotValidException: ", e);
return handleBindingResult(e.getBindingResult());
}
//参数类型错误
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseBody
public Object handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
log.error("MethodArgumentTypeMismatchException: ", e);
return ApiRestResponse.error(ApiExceptionEnum.PARAM_TYPE_ERROR.getCode(), "参数"+e.getName() + ":" + "字段类型错误");
}
//参数缺少
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public Object handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
log.error("MissingServletRequestParameterException: ", e);
return ApiRestResponse.error(ApiExceptionEnum.PARAM_TYPE_ERROR.getCode(), e.getMessage());
}
private ApiRestResponse handleBindingResult(BindingResult result) {
//把异常处理为对外暴露的提示
List<String> list = new ArrayList<>();
if (result.hasErrors()) {
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError objectError : allErrors) {
String message = objectError.getDefaultMessage();
list.add(message);
}
}
if (list.size() == 0) {
return ApiRestResponse.error(ApiExceptionEnum.REQUEST_PARAM_ERROR);
}
return ApiRestResponse
.error(ApiExceptionEnum.REQUEST_PARAM_ERROR.getCode(), list.toString());
}
}
3.3 测试使用
![image-20210123101757499](https://i.loli.net/2021/01/23/dj3iLrMueaDWcZH.png)
![image-20210123101857987](https://i.loli.net/2021/01/23/J4hVkvpFAq9ELuX.png)
4. 后期迭代思路
-
全局错误拦截类中,可以根据请求的Content-Type为json或html做不同处理。返回json或通用模板页面
-
后期项目如果做大,错误码的定义可以用一个专门的配置文件记录,再通过一个类映射相应的值。可以方便国际化!
-
关于错误码的定义和优化可以参照阿里开发手册中的一些设计思路。