业务需求:
客户与项目的交涉的门户是前端的界面,当我们需要给用户反馈一些导致异常的友好提示,确保用户的使用舒适度,避免一报错或者数据采集不正确就弹出突如其来的 500 警告或者一点提示都没有,也没有目标效果出现。而全局异常处理就为了让报错有友好的提示
简单的说全局异常处理就是为了让你女朋友生气的时候能告诉你为什么生气,而不是一句话都不说或者直接红色暴击
如果说在后端这里是报一个异常,那么他弹框出来的数据就是500.....等很不友好的信息,对于用户来说这是很不清晰并且精神伤害很大的,因此我们要把报错的提示消息自定义一下,用温柔的口吻来给予用户最佳的体验,让生气的女友温柔的说明白你做错了啥
搭建步骤:
创建一个自定义异常类
/**
* @author wzx
*/
public class BusinessException extends RuntimeException {
private ResponseCodeEnum codeEnum;
private Integer code;
private String message;
public BusinessException(String message, Throwable e) {
super(message, e);
this.message = message;
}
public BusinessException(String message) {
super(message);
this.message = message;
}
public BusinessException(Throwable e) {
super(e);
}
public BusinessException(ResponseCodeEnum codeEnum) {
super(codeEnum.getMsg());
this.codeEnum = codeEnum;
this.code = codeEnum.getCode();
this.message = codeEnum.getMsg();
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
public ResponseCodeEnum getCodeEnum() {
return codeEnum;
}
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
/**
* 重写fillInStackTrace 业务异常不需要堆栈信息,提高效率.
*/
@Override
public Throwable fillInStackTrace() {
return this;
}
}
搭配一些异常状态枚举类丰富一下
/**
* @author wzx
*/
@Getter
@AllArgsConstructor
public enum ResponseCodeEnum {
CODE_200(200, "请求成功"),
CODE_404(404, "请求地址不存在"),
CODE_600(600, "请求参数错误"),
CODE_601(601, "信息已经存在"),
CODE_500(500, "服务器返回错误,请联系管理员"),
CODE_901(901, "登录超时,请重新登录"),
CODE_902(902, "分享连接不存在,或者已失效"),
CODE_903(903, "分享验证失效,请重新验证"),
CODE_904(904, "网盘空间不足,请扩容");
private Integer code;
private String msg;
}
创建一个统一的响应类
public class ResponseVO<T> {
private String status;
private Integer code;
private String info;
private T data;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
public class ABaseController {
private static final Logger logger = LoggerFactory.getLogger(ABaseController.class);
protected static final String STATUC_SUCCESS = "success";
protected static final String STATUC_ERROR = "error";
protected <T> ResponseVO getSuccessResponseVO(T t) {
ResponseVO<T> responseVO = new ResponseVO<>();
responseVO.setStatus(STATUC_SUCCESS);
responseVO.setCode(ResponseCodeEnum.CODE_200.getCode());
responseVO.setInfo(ResponseCodeEnum.CODE_200.getMsg());
responseVO.setData(t);
return responseVO;
}
创建异常处理接口用于响应异常消息
@RestControllerAdvice
public class AGlobalExceptionHandlerController extends ABaseController {
private static final Logger logger = LoggerFactory.getLogger(AGlobalExceptionHandlerController.class);
@ExceptionHandler(value = Exception.class)
Object handleException(Exception e, HttpServletRequest request) {
logger.error("请求错误,请求地址{},错误信息:", request.getRequestURL(), e);
ResponseVO ajaxResponse = new ResponseVO();
//404
if (e instanceof NoHandlerFoundException) {
ajaxResponse.setCode(ResponseCodeEnum.CODE_404.getCode());
ajaxResponse.setInfo(ResponseCodeEnum.CODE_404.getMsg());
ajaxResponse.setStatus(STATUC_ERROR);
} else if (e instanceof BusinessException) {
//业务错误
BusinessException biz = (BusinessException) e;
ajaxResponse.setCode(biz.getCode() == null ? ResponseCodeEnum.CODE_600.getCode() : biz.getCode());
ajaxResponse.setInfo(biz.getMessage());
ajaxResponse.setStatus(STATUC_ERROR);
} else if (e instanceof BindException|| e instanceof MethodArgumentTypeMismatchException) {
//参数类型错误
ajaxResponse.setCode(ResponseCodeEnum.CODE_600.getCode());
ajaxResponse.setInfo(ResponseCodeEnum.CODE_600.getMsg());
ajaxResponse.setStatus(STATUC_ERROR);
} else if (e instanceof DuplicateKeyException) {
//主键冲突
ajaxResponse.setCode(ResponseCodeEnum.CODE_601.getCode());
ajaxResponse.setInfo(ResponseCodeEnum.CODE_601.getMsg());
ajaxResponse.setStatus(STATUC_ERROR);
} else {
ajaxResponse.setCode(ResponseCodeEnum.CODE_500.getCode());
ajaxResponse.setInfo(ResponseCodeEnum.CODE_500.getMsg());
ajaxResponse.setStatus(STATUC_ERROR);
}
return ajaxResponse;
}
}
@ExceptionHandler : 当系统中抛出指定异常时,该注解可以拦截,ControllerAdvice与@ExceptionHandler注解结合使用,用于全局异常处理。 当系统中抛出指定异常时,该注解可以拦截并进行相应的统一处理。这样可以减少代码冗余,在项目开发中非常常用。
使用方式:
/**
* 保存分享到自己的网盘
*/
@RequestMapping("/saveShare")
@GlobalInterceptor(checkParams = true)
public ResponseVO saveShare(HttpSession session, String shareId, String shareFileIds, String myFolderId) {
try {
SessionShareDto shareSessionDto = checkShare(session, shareId);
SessionWebUserDto webUserDto = getUserInfoFromSession(session);
if (shareSessionDto.getShareUserId().equals(webUserDto.getUserId())) {
throw new BusinessException("自己分享的文件无法保存到自己的网盘");
}
fileInfoService.saveShare(shareSessionDto.getFileId(), shareFileIds, myFolderId, shareSessionDto.getShareUserId(), webUserDto.getUserId());
return getSuccessResponseVO(null);
} catch (BusinessException e) {
// 直接抛出自定义的异常,并将相关想要在前端展示给客户的消息写进去就行
throw new BusinessException("自己分享的文件无法保存到自己的网盘");
}
}
}
直接抛出自定义的异常,并将相关想要在前端展示给客户的消息写进去就行,这样子当出现异常,前端就能获取到你自定义的友好提示信息,用于展示给用户
业务进行流程:
- 后端出现异常
- 抛出自定义异常
- 异常处理器@Exceptional捕获到该异常信息
- 执行异常处理器中自定义的方法
- 将捕捉到的自定义异常中的友好提示信息及其相关的响应码设置到统一的响应类对象中进行响应
- 前端接收到响应数据,提取响应信息用于展示给用户