feign在开发中极大的节省了开发中对接第三方接口的时间,不用在为千奇百怪的三方接口协议去实现不同的请求接口,方便到调用三方接口就像我们自己的接口一样的容易。在大部分情况下不需要特别的处理feign就能很好的为我们工作,然而最进的一个工作让我不得不重新审视feign的使用问题,因为那已经无法完成我的工作了,同时我又不想去手动用httpclien再去写一个请求方法。那是什么问题,我又是怎么解决的呢,下面就简单记录一下。
第一个问题就是在URL中加入了认证信息的,其URL格式这样的http://name:pwd@url.com/a/b,这种如果再用feign去调的话,对方始终是无法调用通过的。默认情况下,feign通过jdk中的HttpURLConnection向下游服务发起http请求,不知道是不是这个原因导致的但这确实不是一个最佳的实践,因此改用功能更强大的httpclient.方法很简单
1.pom文件增加feign-httpclient的依赖(请注意与feign-core的版本保持一致)
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>9.4.0</version>
</dependency>
2.application.properties配置激活
feign.httpclient.enabled=true
其它配置根据自己需要自行配置,我这里都采用的默认配置
还有一种更麻烦也更常见的问题,feign是只会返回状态码为200时的响应体,而在resultful模式的响应中,某些非200的响应体我们也是需要的,这个时候就需要我们重写errordecode,并将返回内容封装到自定义的异常里面,然后在异常回调时把错误返回出来。
重写errordecode
import com.caiyi.financial.nirvana.pay.common.exception.YofishException;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import java.io.IOException;
/**
* @author wxy
* @create 2019-10-23 17:50
**/
public class ErrorDecoderConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new UserErrorDecoder();
}
/**
* 自定义错误解码器
*/
public class UserErrorDecoder implements ErrorDecoder {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public Exception decode(String methodKey, Response response) {
// 自定义异常
YofishException exception = null;
try {
// 获取原始的返回内容
String json = Util.toString(response.body().asReader());
// 业务异常抛出简单的 RuntimeException,保留原来错误信息
exception = new YofishException(String.valueOf(response.status()),json);
} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
return exception;
}
}
}
在需要使用的feign中使用该配置
import com.caiyi.financial.nirvana.pay.common.configuration.KeepErrMsgConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
@FeignClient(name = "RazorpayClient", url = "${razorpay.api.url}", fallbackFactory = RazorpayClientHystric.class, configuration = ErrorDecodeConfiguration.class)
public interface RazorpayApiClient {
/**
* 创建联系人
* @param param
* @return
*/
@PostMapping(value = "/contacts", consumes = MediaType.APPLICATION_JSON_VALUE)
String contracts(@RequestBody Map<String, ?> param);
/**
* 创建资金账户
* @param param
* @return
*/
@PostMapping(value = "/fund_accounts", consumes = MediaType.APPLICATION_JSON_VALUE)
String fundAccounts(@RequestBody Map<String, ?> param);
/**
* 支付
* @param param
* @return
*/
@PostMapping(value = "/payouts", consumes = MediaType.APPLICATION_JSON_VALUE)
String payouts(@RequestBody Map<String, ?> param);
/**
* 订单查询
* @param param
* @return
*/
@GetMapping(value = "/payouts/{tradeNo}", consumes = MediaType.APPLICATION_JSON_VALUE)
String payouts(@PathVariable("tradeNo") String tradeNo);
}
在错误fallback中把错误信息取出
import com.caiyi.financial.nirvana.pay.common.exception.YofishException;
import feign.Response;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author wxy
* @create 2019-10-15 17:15
**/
@Slf4j
@Component
public class RazorpayClientHystric implements FallbackFactory<RazorpayApiClient> {
@Override
public RazorpayApiClient create(Throwable throwable) {
return new RazorpayApiClient() {
@Override
public String contracts(Map<String, ?> param) {
log.error("razorpay|创建联系人网络异常,参数:{}", param, throwable);
if (throwable instanceof YofishException) {
return ((YofishException) throwable).getDesc();
}
return null;
}
@Override
public String fundAccounts(Map<String, ?> param) {
log.error("razorpay|创建资金账户网络异常,参数:{}", param, throwable);
if (throwable instanceof YofishException) {
return ((YofishException) throwable).getDesc();
}
return null;
}
@Override
public String payouts(Map<String, ?> param) {
log.error("razorpay|网络异常,参数:{}", param, throwable);
if (throwable instanceof YofishException) {
return ((YofishException) throwable).getDesc();
}
return null;
}
@Override
public String payouts(String tradeNo) {
log.error("razorpay|询网络异常,参数:{}", tradeNo, throwable);
if (throwable instanceof YofishException) {
return ((YofishException) throwable).getDesc();
}
return null;
}
};
}
}
这里只简单返回里String,也只提供一个思路,希望能起到抛砖引玉的作用,若果大家有其它思路欢迎评论区见。