全局異常處理模版:@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中是否已經定義了
 

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