一、博客背景
在项目中有遇到一个需求,对于项目中的特殊异常信息信息需要做一个处理,统一返回到前端。
二、代码
统一异常处理类
import com.tp.common.constant.CommonConstant;
import com.tp.common.exception.BusinessException;
import com.tp.common.exception.TokenException;
import com.tp.common.model.Resp;
import com.tp.common.utils.IpUtils;
import com.tp.common.utils.RespUtil;
import com.tp.service.threadlocal.UserContext;
import com.tp.web.config.EsLogConfig;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@ControllerAdvice
@Slf4j
public class ExceptionAdvice {
private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);
@Resource
HttpServletRequest httpServletRequest;
@Resource
EsLogConfig esLogConfig;
@ExceptionHandler(Exception.class)
@ResponseBody
public Resp exception(Exception e) {
logger.error("请求出现未知异常", e);
saveLogToEsQueue(e, httpServletRequest, "未知异常,请联系客服!", CommonConstant.ErrorEnum.EXCEPTION_CODE.getCode(), getPhone());
return RespUtil.fail("未知异常,请联系客服!", CommonConstant.ErrorEnum.EXCEPTION_CODE.getCode());
}
@ExceptionHandler(BusinessException.class)
@ResponseBody
public Resp businessException(BusinessException e) {
log.error(e.getMessage(), e);
saveLogToEsQueue(e, httpServletRequest, e.getMessage(), e.getCode(), getPhone());
return RespUtil.fail(e.getMessage(), e.getCode());
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
public Resp IllegalArgumentException(IllegalArgumentException e) {
log.error(e.getMessage(), e);
saveLogToEsQueue(e, httpServletRequest, e.getMessage(), CommonConstant.ErrorEnum.ILLEGALARGUMENT_EXCEPTION_CODE.getCode(), getPhone());
return RespUtil.fail(e.getMessage(), CommonConstant.ErrorEnum.ILLEGALARGUMENT_EXCEPTION_CODE.getCode());
}
@ExceptionHandler({TokenException.class})
@ResponseBody
public Resp tokenException(TokenException e) {
saveLogToEsQueue(e, httpServletRequest, e.getMessage(), e.getCode(), "no login");
return RespUtil.fail(e.getData(), e.getMessage(), e.getCode());
}
@ExceptionHandler({BindException.class})
@ResponseBody
public Resp bindException(BindException e) {
List<ObjectError> objectErrorList = e.getBindingResult().getAllErrors();
StringBuilder message = new StringBuilder();
for (ObjectError objectError : objectErrorList) {
FieldError fieldError = (FieldError) objectError;
message.append(fieldError.getField() + " " + fieldError.getDefaultMessage() + "/n");
}
saveLogToEsQueue(e, httpServletRequest, message.toString(), CommonConstant.ErrorEnum.ILLEGALARGUMENT_EXCEPTION_CODE.getCode(), getPhone());
return RespUtil.fail(message.toString(), CommonConstant.ErrorEnum.ILLEGALARGUMENT_EXCEPTION_CODE.getCode());
}
/**
* 将系统中抛出来的错误日志信息保存到内存队列中
*
* @param e 异常对象
* @param httpServletRequest 请求对象,获取请求的方法和ip地址
* @param message 自定义的错误信息
* @param code 自定义的错误编码
* @param phone 手机号,用来标识用户身份信息
*/
private void saveLogToEsQueue(Exception e, HttpServletRequest httpServletRequest, String message, int code, String phone) {
String requestUri = httpServletRequest.getRequestURL().toString();
String userId = IpUtils.getIpAddr(httpServletRequest) + " : " + phone;
StringBuilder url = new StringBuilder(esLogConfig.getUrl());
url.append(esLogConfig.getDomain())
.append("&expCode=").append(code)
.append("&expMessage=").append(message)
.append("&expStacktrace=").append(e.getStackTrace()[0])
.append("&platform=KSXD")
.append("&requestIp=").append(IpUtils.getIpAddr(httpServletRequest))
.append("&requestUri=").append(requestUri)
.append("&userId=").append(userId);
if (!EsLogQueue.INS.push(url.toString())) {
log.error("将异常信息添加到内存队列失败,队列已满!");
}
}
/**
* 获取手机号
*
* @return 手机号
*/
private String getPhone() {
String phone;
try {
phone = UserContext.getUser().getPhone();
} catch (Exception exception) {
phone = "no login";
}
return phone;
}
}
三、代码讲解
-
ControllerAdvice, 类注解, ControllerAdvice 注解定义了一个全局的异常处理器。(该注解还有全局数据绑定、全局数据预处理的功能,可自行了解)可以指定处理的controller范围:有以下几个属性
basePackages:指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理
basePackageClasses:是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理
assignableTypes:指定一个或多个 Controller 类,这些类被该 @ControllerAdvice 管理
annotations:指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理
-
ExceptionHandler, 方法注解, 定义该方法为异常处理方法。value 的值为需要处理的异常类的 class 文件。
从上面的代码中可以看出,代码里有对几种异常做处理,而需要达到将异常信息抛出到前端也只需要在controller中将异常抛出即可,这是因为ControllerAdvice只会拦截controller里的异常,无论是controller本身抛出还是service中抛出到controller里。
在代码中@ExceptionHandler(BusinessException.class)说明该方法用于处理BusinessException这一类型异常。