全局异常处理模版:@ControllerAdvice/@RestControllerAdvice

场景:在日常代码中,后端同学与前端同学交互过程中,异常的返回总是要遵循一套规定。后端的调用不同的rpc接口,异常的反馈总是不一样,所以要统一处理,下面是比较通用的模式:采用@ControllerAdvice

异常通用处理方法:

import com.dianping.credit.audit.disposal.exception.DisposeException;
import com.dianping.credit.audit.disposal.web.enums.ResponseEnum;
import com.dianping.credit.audit.disposal.web.vo.Response;
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.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * 全局异常处理
 */
@Slf4j
@ControllerAdvice
public class WebExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Response handleException(Exception e) {
        log.error("handleException Internal exception:", e);
        return Response.getFail(ResponseEnum.ERROR);
    }



    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Response handleArgumentException(MethodArgumentNotValidException e) {
        FieldError error = e.getBindingResult().getFieldError();
        String message = error != null ? error.getField() + ": " + error.getDefaultMessage() : null;
        log.error("MethodArgumentNotValidException:",e);
        return Response.getFail(ResponseEnum.INVALID_PARAM.getCode(), message);
    }

    @ResponseBody
    @ExceptionHandler({IllegalArgumentException.class})
    public Response handleIllegalArgumentException(IllegalArgumentException e) {
        log.error("handleIllegalArgumentException:",e);
        return Response.getFail(ResponseEnum.INVALID_PARAM);
    }

    /**
     * 自定义异常
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler({DisposeException.class})
    public Response handleDisposeException(DisposeException e) {
        log.error("DisposeException:",e);
        return Response.getFail(e.getCode(),e.getMessage());
    }
}

自定义异常:

import lombok.Data;

import java.io.Serializable;

/**
 * 处置平台异常
 * @Author: dainan
 * @Date: 2019/9/18 17:11
 * @Description:
 */
@Data
public class DisposeException extends RuntimeException implements Serializable {

    private int code;

    public DisposeException(String message) {
        super(message);
    }

    public DisposeException(int code, String message) {
        super(message);
        this.code = code;
    }


    public static DisposeException of(Throwable t){
        return new DisposeException(t.getMessage());
    }

    @Override
    public String toString() {
        return String.format("DisposalException[code=%s,message=%s]", code, super.getMessage());
    }
}

 

常见异常code自定义:

import java.io.Serializable;

/**
 * @Author: dainan
 * @Date: 2019/12/27 17:38
 * @Description:
 */
public class DisposeExceptionCode implements Serializable {
    /**
     * 成功
     */
    public static final int SUCCESS = 10000;
    /**
     * 系统错误
     */
    public static final int SYSTEM_ERROR = 10001;
    /**
     * 服务超时
     */
    public static final int TIMEOUT = 10002;
    /**
     * 服务限流
     */
    public static final int SERVICE_LIMIT = 10003;
    /**
     * 参数错误
     */
    public static final int ERROR_PARAMETER = 10004;
    /**
     * 接口不存在
     */
    public static final int INTEFACE_NOT_EXIST = 10005;
    /**
     * HTTP METHOD不支持
     */
    public static final int HTTP_NOT_SUPPORT = 10006;
    /**
     * 非法请求
     */
    public static final int ILLEGAL_REQUEST = 10007;
    /**
     * 非法用户
     */
    public static final int ILLEGAL_USER = 10008;
    /**
     * 没有权限
     */
    public static final int NO_PERMISSION = 10009;
}

在某些情况下全局异常处理没有成功:

解决办法:

1.确保注解@RestControllerAdvice/@ControllerAdvice的类被spring容器管理到。

    ①spring boot Java配置检查@SpringBootApplication(scanBasePackages = )(scanBasePackages 配置的包是否包含这个类默认情况下spring boot项目扫描的是@SpringBootApplication注解所在类的包及子包)
    ② xml配置的spring 普通项目检查<context:component-scan base-package="com.test"/>

2.检查项目中所有的切面编程,是否在某个切面将异常try-catch然后没有扔出来。常见的就是切面的环绕处理,捕获了异常忘记抛出来。
楼主碰到了这个问题:

@Around("beforePointcut()")
    public Object doAccessCheck(ProceedingJoinPoint joinPoint) throws Throwable {
        Object object;
        UserInfo userInfo = getUserInfoFromSso(joinPoint.getArgs());
        if (userInfo == null) {
            return ConstantUtil.paraError(StatusCodeEnum.AUTH_FAIL);
        }
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            setUserInfo(args[0], userInfo);
        }
        try {
            object = joinPoint.proceed();
            daDian(joinPoint, userInfo.getRealName(), userInfo.getLoginName(), object);
        } catch (Throwable e) {
            LOGGER.error("【" + joinPoint.getSignature() + "方法发生异常】" + "【异常报告:" + e.getCause() + "】", e);
            throw e;//如果这个地方没有抛出,就没法捕获这个异常,全局异常处理就不会有效果
        }
        return object;
    }


3.检查项目中是否有其他的相同的全局异常处理类,例如BaseController中是否已经定义了
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章