場景:在日常代碼中,後端同學與前端同學交互過程中,異常的返回總是要遵循一套規定。後端的調用不同的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中是否已經定義了