spring boot项目使用@ControllerAdvice做全局异常处理

一、博客背景

在项目中有遇到一个需求,对于项目中的特殊异常信息信息需要做一个处理,统一返回到前端。

二、代码

统一异常处理类

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 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这一类型异常。

 

你可能感兴趣的:(工作总结,全局异常处理)