Feign调用接口解决处理内部异常的问题
问题描述:
当使用feign调用接口,出现400~500~的接口问题时。会出错feign:FeignException。(因为是错误,只能用catch Throwable,不可使用catch Exception捕获异常)导致程序无法继续运行。
问题原因:
【Feign调用接口解决处理内部异常的问题】由于feign默认的错误处理类是FunFeignFallback会throw new AfsBaseExceptio导致外部无法捕获异常。
package com.ruicar.afs.cloud.common.core.feign; import com.alibaba.fastjson.JSONObject; import com.ruicar.afs.cloud.common.core.exception.AfsBaseException; import com.ruicar.afs.cloud.common.core.util.IResponse; import feign.FeignException; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import org.springframework.lang.Nullable; import java.lang.reflect.Method; import java.util.Objects; @Data@AllArgsConstructor@Slf4jpublic class FunFeignFallbackimplements MethodInterceptor {private final Class targetType; private final String targetName; private final Throwable cause; private static byte JSON_START = '{'; @Nullable@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {String errorMessage = cause.getMessage(); if (!(cause instanceof FeignException)) {log.error("FunFeignFallback:[{}.{}] serviceId:[{}] message:[{}]", targetType.getName(), method.getName(), targetName, errorMessage); log.error("feign调用失败", cause); return IResponse.fail("请求失败,请稍后再试"); }int status = ((FeignException.FeignClientException) this.cause).status(); boolean isAuthFail = (status==426||status==403||status==401)&&"afs-auth".equals(targetName); FeignException exception = (FeignException) cause; if(isAuthFail){log.warn("授权失败==========原始返回信息:[{}]",exception.contentUTF8()); }else {log.error("FunFeignFallback:[{}.{}] serviceId:[{}] message:[{}]", targetType.getName(), method.getName(), targetName, errorMessage); log.error("", cause); log.error("原始返回信息{}",exception.contentUTF8()); }if(method.getReturnType().equals(Void.class)){throw new AfsBaseException("接口调用失败"); }if(method.getReturnType().equals(IResponse.class)){if(exception instanceof FeignException.Forbidden){return IResponse.fail("没有权限").setCode("403"); }if(exception instanceof FeignException.NotFound){return IResponse.fail("请求路径不存在").setCode("404"); }if(exception instanceof FeignException.BadRequest){return IResponse.fail("参数错误").setCode("400"); } if(exception.content()==null||exception.content().length==0){return IResponse.fail("请求失败,请稍后再试"); }if(JSON_START==exception.content()[0]){return JSONObject.parseObject(exception.content(),IResponse.class); }else{return IResponse.fail(exception.contentUTF8()); }}else{try {if(method.getReturnType().equals(String.class)){return exception.contentUTF8(); }else if(method.getReturnType().equals(JSONObject.class)){if(JSON_START==exception.content()[0]){return JSONObject.parseObject(exception.content(), JSONObject.class); }}else if(!method.getReturnType().equals(Object.class)){return JSONObject.parseObject(exception.content(), method.getReturnType()); }if(JSON_START==exception.content()[0]){JSONObject jsonObject = JSONObject.parseObject(exception.content(), JSONObject.class); if(jsonObject.containsKey("code")&&jsonObject.containsKey("msg")) {return jsonObject.toJavaObject(IResponse.class); }}}catch (Throwable e){}throw new AfsBaseException("接口调用失败"); }} @Overridepublic boolean equals(Object o) {if (this == o) {return true; }if (o == null || getClass() != o.getClass()) {return false; }FunFeignFallback> that = (FunFeignFallback>) o; return targetType.equals(that.targetType); } @Overridepublic int hashCode() {return Objects.hash(targetType); } }
问题解决:自定义feignFallback异常处理: 1.自定义异常处理 InvoiceApiFeignFallbackFactory
package com.ruicar.afs.cloud.invoice.factory; import com.ruicar.afs.cloud.invoice.fallback.InvoiceApiFeignFallback; import com.ruicar.afs.cloud.invoice.feign.InvoiceApiFeign; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; @Componentpublic class InvoiceApiFeignFallbackFactory implements FallbackFactory{@Overridepublic InvoiceApiFeign create(Throwable throwable) {InvoiceApiFeignFallback invoiceApiFeignFallback = new InvoiceApiFeignFallback(); invoiceApiFeignFallback.setCause(throwable); return invoiceApiFeignFallback; }}
2.feign调用 InvoiceApiFeignFallbackFactory
package com.ruicar.afs.cloud.invoice.feign; import com.alibaba.fastjson.JSONObject; import com.ruicar.afs.cloud.common.core.feign.annotations.AfsFeignClear; import com.ruicar.afs.cloud.invoice.dto.InvoiceCheckDto; import com.ruicar.afs.cloud.invoice.factory.InvoiceApiFeignFallbackFactory; import io.swagger.annotations.ApiOperation; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import java.util.Map; /** * @description: 发票验证接口 * @author: rongji.zhang * @date: 2020/8/14 10:32 */@FeignClient(name = "invoice", url = "${com.greatwall.systems.invoice-system.url}" ,fallbackFactory = InvoiceApiFeignFallbackFactory.class)public interface InvoiceApiFeign {/**** @param dto* @return*/@ApiOperation("获取业务数据API接口")@PostMapping(value = "https://www.it610.com/vi/check")@AfsFeignClear(true)//通过此注解防止添加内部tokenJSONObject InvoiceCheck(@RequestBody InvoiceCheckDto dto, @RequestHeader Map headers); }
3.实现自定义报错处理
package com.ruicar.afs.cloud.invoice.fallback; import com.alibaba.fastjson.JSONObject; import com.ruicar.afs.cloud.invoice.dto.InvoiceCheckDto; import com.ruicar.afs.cloud.invoice.feign.InvoiceApiFeign; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.Map; /** * @author Fzero * @date 2019-01-24 */@Slf4j@Componentpublic class InvoiceApiFeignFallback implements InvoiceApiFeign { @Setterprivate Throwable cause; /*** @param dto* @param headers* @return*/@Overridepublic JSONObject InvoiceCheck(InvoiceCheckDto dto, Map headers) {log.error("feign 接口调用失败", cause); return null; }}
Feign远程调用失败-----丢请求头
@FeignClient("guli-cart")public interface CartFenignService { @GetMapping("/currentUserCartItems")ListgetCurrentUserCartItems(); }// 这样去掉接口时其实Feign在底层是一个全新的requst所有请求头就没有了
解决办法使用Feign远程掉用拦截器,在远程请求是先创建拦截器
@Bean("requestInterceptor")public RequestInterceptor requestInterceptor() {return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) { /*** 把以前的Cookie放到新请求中去原理就是运用了同一线程数据共享ThreadLocal*/ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String cookie = request.getHeader("Cookie"); template.header("Cookie", cookie); }}; }
但是上面的办法只能解决同意线程问题,在多线程下还是会丢失请求头
多线程下解决办法:
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
把请求单独拿出来给每个线程单独
RequestContextHolder.setRequestAttributes(requestAttributes);
这样就可以了~
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 基于微信小程序带后端ssm接口小区物业管理平台设计
- 2020-04-07vue中Axios的封装和API接口的管理
- SpringBoot调用公共模块的自定义注解失效的解决
- thinkphp|thinkphp 3.2 如何调用第三方类库
- 调取接口时报404错误(ID:16)
- CICC(脑机接口,科幻几近成真())
- 分享!如何分分钟实现微信扫二维码调用外部浏览器打开指定页面的功能
- 接口|axios接口报错-参数类型错误解决
- WKWebview|WKWebview js 调用oc 和oc调用js