一、背景
使用统一返回结果时,还有一种情况,就是程序的报错是由于运行时异常导致的结果,有些异常是我们在业务中抛出的,有些是无法提前预知。
因此,我们需要定义一个统一的全局异常,在Controller
捕获所有异常,并且做适当处理,并作为一种结果返回。
@ExceptionHandler
可以处理异常, 但是仅限于当前Controller
中处理异常;@ControllerAdvice
可以配置basePackage
下的所有Controller
。结合两者使用,就可以处理全局的异常了。
二、全局异常处理
2.1 设计思路:
- 自定一个异常类(如:
TokenVerificationException
),捕获针对项目或业务的异常; - 使用
@ExceptionHandler
注解捕获自定义异常和通用异常; - 使用
@ControllerAdvice
集成@ExceptionHandler
的方法到一个类中; - 异常的对象信息补充到统一结果枚举中;
2.2 自定义异常
public class TokenVerificationException extends RuntimeException {
/**
* 错误码
*/
protected Integer code;
protected String msg;
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
/**
* 有参构造器,返回码在枚举类中,这里可以指定错误信息
* @param msg
*/
public TokenVerificationException(String msg) {
super(msg);
}
}
2.3 统一异常处理器
@ControllerAdvice
注解是一种作用于控制层的切面通知(Advice
),能够将通用的@ExceptionHandler
、@InitBinder
和@ModelAttributes
方法收集到一个类型,并应用到所有控制器上。
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常捕获
* @param e 捕获的异常
* @return 封装的返回对象
**/
@ExceptionHandler(Exception.class)
public HttpResult handlerException(Exception e) {
ResultCodeEnum resultCodeEnum;
// 自定义异常
if (e instanceof TokenVerificationException) {
resultCodeEnum = ResultCodeEnum.TOKEN_VERIFICATION_ERROR;
resultCodeEnum.setMessage(getConstraintViolationErrMsg(e));
log.error("tokenVerificationException:{}", resultCodeEnum.getMessage());
}else {
// 其他异常,当我们定义了多个异常时,这里可以增加判断和记录
resultCodeEnum = ResultCodeEnum.SERVER_ERROR;
resultCodeEnum.setMessage(e.getMessage());
log.error("common exception:{}", JSON.toJSONString(e));
}
return HttpResult.failure(resultCodeEnum);
}
/**
* 获取错误信息
* @param ex
* @return
*/
private String getConstraintViolationErrMsg(Exception ex) {
// validTest1.id: id必须为正数
// validTest1.id: id必须为正数, validTest1.name: 长度必须在有效范围内
String message = ex.getMessage();
try {
int startIdx = message.indexOf(": ");
if (startIdx < 0) {
startIdx = 0;
}
int endIdx = message.indexOf(", ");
if (endIdx < 0) {
endIdx = message.length();
}
message = message.substring(startIdx, endIdx);
return message;
} catch (Throwable throwable) {
log.info("ex caught", throwable);
return message;
}
}
}
- 说明
- 我使用的是
@RestControllerAdvice
,等同于@ControllerAdvice
+@ResponseBody
- 错误枚举类这里省略了,详见Github代码。
三、测试及总结
3.1 测试接口
@RestController
@RequestMapping("/exception")
@Api(tags = "异常测试接口")
public class ExceptionRestController {
@ApiOperation(value = "业务异常(token 异常)", httpMethod = "GET")
@GetMapping("/token")
public HttpResult token() {
// 模拟业务层抛出 token 异常
throw new TokenVerificationException("token 已经过期");
}
@ApiOperation(value = "其他异常", httpMethod = "GET")
@GetMapping("/errorException")
public HttpResult errorException() {
//这里故意造成一个其他异常,并且不进行处理
Integer.parseInt("abc123");
return HttpResult.success();
}
}
3.2 返回结果
http://localhost:8080/swagger-ui.html#/
{
"code": 500,
"message": "For input string: \"abc123\"",
"success": false
}
{
"code": 4000,
"message": "token 已经过期",
"success": false
}
3.3 总结
@RestControllerAdvice
和@ExceptionHandler
会捕获所有Rest
接口的异常并封装成我们定义的HttpResult
的结果集返回,但是:处理不了拦截器里的异常
3.1 日常求赞
博主祖传秘籍 Spring Boot 葵花宝典 开源中,欢迎前来吐槽,提供线索,告诉博主接下来更新哪方面文章,共同进步!
3.2 文化交流
最新文章,欢迎关注:公众号-风尘博客;交流观点,欢迎添加:个人微信