SpringBoot全局异常统一处理
目标:
对运行时发生的异常进行统一处理
处理自定义异常
Validator统一异常封装
Assert的异常统一封装
一:定义异常统一返回的格式规范:
import com.techsun.industry.common.enums.ResultCode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ErrorResult {
/**
* 异常状态码
*/
private Integer status;
/**
* 用户看得见的异常,例如 用户名重复!!,
*/
private String message;
/**
* 异常的名字
*/
private String exception;
/**
* 异常堆栈信息
*/
//private String errors;
/**
* 对异常提示语进行封装
*/
public static ErrorResult fail(ResultCode resultCode, Throwable e, String message) {
ErrorResult result = ErrorResult.fail(resultCode, e);
if(StringUtils.isNotBlank(message)){
result.setMessage(message);
}
return result;
}
/**
* 对异常枚举进行封装
*/
public static ErrorResult fail(ResultCode resultCode, Throwable e) {
ErrorResult result = new ErrorResult();
result.setMessage(resultCode.message());
result.setStatus(resultCode.code());
result.setException(e.getClass().getName());
//result.setErrors(Throwables.getStackTraceAsString(e));
return result;
}
}
二:自定义异常用来处理业务逻辑中的异常情况
import com.techsun.industry.common.enums.ResultCode;
import lombok.Data;
/**
* 自定义异常
*/
@Data
public class BusinessException extends RuntimeException{
public Integer code;
public String message;
public BusinessException(ResultCode resultCode) {
this.code = resultCode.code();
this.message = resultCode.message();
}
}
三:编写全局异常处理类
package com.*.*.config.aspect;
import com.techsun.industry.common.base.response.ErrorResult;
import com.techsun.industry.common.enums.ResultCode;
import com.techsun.industry.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestControllerAdvice
@Slf4j
/**
* 全局异常处理
*/
public class GlobalExceptionHandler {
/**
* 处理运行时异常
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ErrorResult handleThrowable(Throwable e, HttpServletRequest request) {
//TODO 运行时异常,可以在这里记录,用于发异常邮件通知
ErrorResult error =ErrorResult.fail(ResultCode.SYSTEM_ERROR, e);
log.error("URL:{} ,系统异常: ",request.getRequestURI(), e);
return error;
}
/**
* 处理自定义异常
*/
@ExceptionHandler(BusinessException.class)
public ErrorResult handleBusinessException(BusinessException e, HttpServletRequest request) {
ErrorResult error = ErrorResult.builder().status(e.code)
.message(e.message)
.exception(e.getClass().getName())
.build();
log.warn("URL:{} ,业务异常:{}", request.getRequestURI(),error);
return error;
}
/**
* validator 统一异常封装
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
String msgs = this.handle(e.getBindingResult().getFieldErrors());
ErrorResult error = ErrorResult.fail(ResultCode.PARAM_IS_INVALID, e, msgs);
log.warn("URL:{} ,参数校验异常:{}", request.getRequestURI(),msgs);
return error;
}
private String handle(List<FieldError> fieldErrors) {
StringBuilder sb = new StringBuilder();
for (FieldError obj : fieldErrors) {
sb.append(obj.getField());
sb.append("=[");
sb.append(obj.getDefaultMessage());
sb.append("] ");
}
return sb.toString();
}
/**
* Assert的异常统一封装
*/
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
ErrorResult error = ErrorResult.builder().status(4000)
.message(e.getMessage())
.exception(e.getClass().getName())
.build();
log.warn("URL:{} ,业务校验异常:{}", request.getRequestURI(),e);
return error;
}
}
对应ResultCode枚举类:
public enum ResultCode {
/* 成功状态码 */
SUCCESS(200, "成功"),
/* 系统500错误*/
SYSTEM_ERROR(10000, "系统异常,请稍后重试"),
/* 参数错误:10001-19999 */
PARAM_IS_INVALID(10001, "参数无效"),
/* 用户错误:20001-29999*/
USER_HAS_EXISTED(20001, "用户已存在"),
USER_LOGIN_FAIL(20002,"账号或密码错误"),
USER_HAS_EXIST(20003,"账号已存在"),
USER_NOT_EXIST(20003,"用户不存在,请重新登录"),
/* 认证失败错误:30001-39999*/
NO_TOKEN(30001,"无token,请重新登录"),
TOKEN_OUT_TIME(30002,"token超时,请重新登录"),
TOKEN_ILLEGAL(30003,"token 认证失败"),
/* 文件失败错误:40001-49999*/
EXCEL_NO_SHEET(40001,"Excel无Sheet");
private Integer code;
private String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer code() {
return this.code;
}
public String message() {
return this.message;
}
public static String getMessage(String name) {
for (ResultCode item : ResultCode.values()) {
if (item.name().equals(name)) {
return item.message;
}
}
return name;
}
public static Integer getCode(String name) {
for (ResultCode item : ResultCode.values()) {
if (item.name().equals(name)) {
return item.code;
}
}
return null;
}
@Override
public String toString() {
return this.name();
}
}
可根据实际情况将Code状态码进行划分。
我们可以参考下Http请求返回的状态码:
下面是常见的HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
我们可以参考这样的设计,这样的好处就把错误类型归类到某个区间内,如果区间不够,可以设计成4位数。
#1000~1999 区间表示参数错误
#2000~2999 区间表示用户错误
#3000~3999 区间表示接口异常
这样前端开发人员在得到返回值后,根据状态码就可以知道,大概什么错误,再根据message相关的信息描述,可以快速定位。
现在可以进行测试了.
还没有评论,来说两句吧...