Feign調(diào)用全局異常處理解決方案
異常信息形如:
TestService#addRecord(ParamVO) failed and no fallback available.;
對(duì)于failed and no fallback available.這種異常信息,是因?yàn)轫?xiàng)目開啟了熔斷:
feign.hystrix.enabled: true
當(dāng)調(diào)用服務(wù)時(shí)拋出了異常,卻沒有定義fallback方法,就會(huì)拋出上述異常。由此引出了第一個(gè)解決方式。
解決方案:
自定義Feign解析器:
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.crecgec.baseboot.jsoncore.exception.BaseException;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
try {
// 這里直接拿到我們拋出的異常信息
String message = Util.toString(response.body().asReader());
try {
JSONObject jsonObject = JSONObject.parseObject(message);
return new BaseException(jsonObject.getString("resultMsg"), jsonObject.getInteger("resultCode"));
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException ignored) {
}
return decode(methodKey, response);
}
}
定義系統(tǒng)的異常類
public class BaseException extends RuntimeException {
private int status ;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public BaseException() {
}
public BaseException(String message, int status) {
super(message);
this.status = status;
}
public BaseException(String message) {
super(message);
}
public BaseException(String message, Throwable cause) {
super(message, cause);
}
public BaseException(Throwable cause) {
super(cause);
}
public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
統(tǒng)一異常攔截轉(zhuǎn)換對(duì)應(yīng)的異常信息返回前端
public class ResultSet {
/**
* 返回的狀態(tài)碼
*/
private Integer resultCode;
/**
* 返回的消息
*/
private String resultMsg;
/**
* 返回的數(shù)據(jù)
*/
private Object data;
public ResultSet() {
}
public ResultSet(Integer resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
public ResultSet(Integer resultCode, String resultMsg, Object data) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
this.data = data;
}
public Integer getResultCode() {
return resultCode;
}
public void setResultCode(Integer resultCode) {
this.resultCode = resultCode;
}
public String getResultMsg() {
return resultMsg;
}
public void setResultMsg(String resultMsg) {
this.resultMsg = resultMsg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
全局異常類處理配置:
@ExceptionHandler(value = BaseException.class)
public ResultSet defaultErrorHandler(HttpServletRequest req, HttpServletResponse resp, BaseException e) {
ResultSet resultSet = new ResultSet();
if (e.getStatus() == 400) {
resultSet.setResultCode(-1);
resultSet.setResultMsg(e.getMessage());
resultSet.setData(null);
resp.setStatus(400);
} else {
resp.setStatus(500);
if(logger.isErrorEnabled()){
logger.error("系統(tǒng)異常,請(qǐng)聯(lián)系系統(tǒng)開發(fā)人員進(jìn)行處理", e);
}
resultSet.setResultCode(-1);
resultSet.setResultMsg(e.getMessage());
resultSet.setData(null);
}
return resultSet;
}
這樣就能完成了feign接收異常處理的自定義異常信息!
統(tǒng)一處理@FeignClient調(diào)用接口異常----原樣拋出
第三方系統(tǒng)調(diào)用我方系統(tǒng)@FeignClient接口時(shí)報(bào)錯(cuò)
com.netflix.hystrix.exception.HystrixRuntimeException: WorkFlowTaskOperateService#processWorkFlowTaskSyncCallback(TaskProcessDTO) failed and no fallback available.
我方系統(tǒng)出現(xiàn)FeignException.

第三方調(diào)用者拋出的異常:HystrixRuntimeException

一檢查我們系統(tǒng)確實(shí)沒有指定fallback和configuration,并且調(diào)用方開啟了feign.hystrix.enabled: true
@FeignClient(value = "taxplan-workflow")
修改方法:
第三方調(diào)用在Application.java添加處理Feign異常的全局處理方法
@Bean
public Feign.Builder feignBuilder() {
return Feign.builder().requestInterceptor(new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
Map<String, String> customHeaders = WebUtils.getCustomHeaders();
customHeaders.forEach((k, v) -> {
requestTemplate.header(k, v);
});
}
}).errorDecoder(new CustomErrorDecoder());
}
這里使用了RequestInterceptor攔截器,可以定制請(qǐng)求頭,如果不想定制,可以改為
return Feign.builder().errorDecoder(new CustomErrorDecoder());
實(shí)現(xiàn)ErrorDecoder接口,其中ExceptionCode是枚舉類.
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
return new BaseBizException(ExceptionCode.CALL_INNER_ERROR, "Client error.httpStatusCode:" + response.status());
} else {
if (response.status() >= 500 && response.status() <= 599 && response.body() != null) {
try {
String content = CharStreams.toString(new InputStreamReader(response.body().asInputStream(), StandardCharsets.UTF_8));
Map responseBody = (Map) JSONObject.parseObject(content, Map.class);
if (responseBody.containsKey("code")) {
return new BaseBizException(responseBody.get("code").toString(), Objects.toString(responseBody.get("msg")));
}
} catch (Exception var5) {
}
}
return new BaseBizException(ExceptionCode.CALL_INNER_ERROR);
}
}
ExceptionCode枚舉類如下:可以自定義增加刪除
public enum ExceptionCode {
ILLEGAL_STATE(4001, "非法訪問"),
PARAM_REQUIRED(4002, "參數(shù)不能為空"),
PARAM_FORMAT_ILLEGAL(4003, "參數(shù)格式錯(cuò)誤"),
REQUEST_DATA_DUPLICATION(4004, "重復(fù)請(qǐng)求"),
REQUEST_DATA_ERROR(4005, "請(qǐng)求數(shù)據(jù)錯(cuò)誤"),
REQUEST_DATA_NOT_MATCH(4006, "請(qǐng)求數(shù)據(jù)不一致"),
RECORD_NOT_EXIST(5001, "記錄不存在"),
RECORD_EXISTED(5002, "記錄已存在"),
RECORD_ILLEGAL_STATE(5003, "數(shù)據(jù)異常"),
BALANCE_NOT_ENOUGH(5103, "余額不足"),
CALL_INNER_ERROR(5800, "調(diào)用內(nèi)部服務(wù)接口異常"),
THIRD_PART_ERROR(5801, "調(diào)用第三方接口異常"),
SYSTEM_ERROR(9999, "系統(tǒng)異常");
public final int code;
public final String defaultMessage;
private ExceptionCode(int code, String defaultMessage) {
this.code = code;
this.defaultMessage = defaultMessage;
}
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
idea新建springboot項(xiàng)目pom文件報(bào)錯(cuò)問題及解決
這篇文章主要介紹了idea新建springboot項(xiàng)目pom文件報(bào)錯(cuò)問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
java sql ResultSet 之getRow()用法說明
這篇文章主要介紹了java sql ResultSet 之getRow()用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08
使用JSON.toJSONString格式化成json字符串時(shí)保留null屬性
這篇文章主要介紹了使用JSON.toJSONString格式化成json字符串時(shí)保留null屬性,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
五分鐘解鎖springboot admin監(jiān)控新技巧
本文不會(huì)講如何搭建企業(yè)的運(yùn)維監(jiān)控系統(tǒng),有興趣的可以去找找成熟的比如Zabbix、Prometheus,甚至比較簡(jiǎn)單的Wgcloud都能滿足一定的需求,不在此贅述。本文講解如何使用Springboot admin對(duì)spring boot項(xiàng)目進(jìn)行應(yīng)用監(jiān)控,感興趣的朋友一起看看吧2021-06-06
詳解Java synchronized關(guān)鍵字的用法
在多線程編程中常常使用鎖機(jī)制來確保同一時(shí)刻只有一個(gè)線程能夠修改共享內(nèi)存,在Java中一般是使用synchronized作為鎖機(jī)制,下面就讓我們來學(xué)習(xí)一下如何使用synchronized實(shí)現(xiàn)線程安全吧2023-08-08
SpringBoot項(xiàng)目中jar發(fā)布獲取jar包所在目錄路徑的最佳方法
在開發(fā)過程中,我們經(jīng)常要遇到上傳圖片、word、pdf等功能,但是當(dāng)我們把項(xiàng)目打包發(fā)布到服務(wù)器上時(shí),對(duì)應(yīng)的很多存儲(chǔ)路徑的方法就會(huì)失效,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目中jar發(fā)布獲取jar包所在目錄路徑的相關(guān)資料2022-07-07
SpringMvc+POI處理excel表數(shù)據(jù)導(dǎo)入
這篇文章主要為大家詳細(xì)介紹了SpringMvc+POI處理excel表數(shù)據(jù)導(dǎo)入,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
手把手教你idea中創(chuàng)建一個(gè)javaweb(webapp)項(xiàng)目詳細(xì)圖文教程
這篇文章主要介紹了如何使用IntelliJ?IDEA創(chuàng)建一個(gè)Maven項(xiàng)目,并配置Tomcat服務(wù)器進(jìn)行運(yùn)行,過程包括創(chuàng)建項(xiàng)目、配置運(yùn)行環(huán)境、部署項(xiàng)目以及測(cè)試運(yùn)行,需要的朋友可以參考下2025-01-01
java并發(fā)學(xué)習(xí)-CountDownLatch實(shí)現(xiàn)原理全面講解
這篇文章主要介紹了java并發(fā)學(xué)習(xí)-CountDownLatch實(shí)現(xiàn)原理全面講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02

