2018-03-29 SpringCloud Feign Decoder
如題:這篇文章主要講的是Spring Cloud Feign的Decoder
前提:使用FeignClient
並且返回值 使用泛型,例如 PhpResponse<Model>。
SpringCloud版本:Brixton (Spring Core 4.2.3)
在HTTP協議不是很規範的情況下,需要配置Decoder
例如PHP寫的服務,就不跟你區分 ContentType 是不是JSON了。即便是大廠騰訊,相信他們的介面例如微信支付等等,是不區分的。
具體來說:就是返回資料是JSON,而ContentType 為 text/html;charset=UTF-8
這不影響你讀取他的文字內容。
對於SpringCloud Feign來說:
在沒有寫Fallback的情況下:
Could not extract response: no suitable HttpMessageConverter found
如果寫了Fallback,則即便返回了正常的JSON內容,因為Decoder無法decode這種Response型別,則進入了Fallback降級類了。你看不到相關的錯誤日誌,警告都沒有。
問題現狀
如果是自己的公司,要求PHP傳 正確的ContentType ,不難,就是要說服他們改改。
也就一句話
header("ContentType", "application/json;charset=UTF-8");
而如果對方很調皮,表示怎麼就你JAVA的要求這麼高? Customer端也沒有要求這個?
而如果對方是騰訊的微信支付介面,你讓人家改?
所以,還是要自己用一些方法處理的。
如果因此放棄Feign ,改用HTTPClient,這也不甘心啊。
解決方案
貢獻出程式碼:
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}
public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new PhpMappingJackson2HttpMessageConverter());
return new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return httpMessageConverters;
}
};
}
public class PhpMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
PhpMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8")); //關鍵
setSupportedMediaTypes(mediaTypes);
}
}
}
這裡關鍵位置,就新增了對這種Response的支援。
RestTemplate的配置,我還不會,這裡就不寫了。
思考
好像配置過FastJSON的MessageConverter的?
下面的內容將說明 我所用的FastJsonHttpMessageConverter沒有起作用的原因。
原理
下面看看原始碼:
//SpringDecoder.java
@Override
public Object decode(final Response response, Type type) throws IOException,
FeignException {
if (type instanceof Class || type instanceof ParameterizedType) {
@SuppressWarnings({ "unchecked", "rawtypes" })
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(
type, this.messageConverters.getObject().getConverters());
return extractor.extractData(new FeignResponseAdapter(response));
}
throw new DecodeException(
"type is not an instance of Class or ParameterizedType: " + type);
}
這裡構造了一個HttpMessageConverterExtractor,跟進建構函式:
HttpMessageConverterExtractor(Type responseType,
List<HttpMessageConverter<?>> messageConverters, Log logger) {
Assert.notNull(responseType, "'responseType' must not be null");
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.responseType = responseType;
this.responseClass = (responseType instanceof Class) ? (Class<T>) responseType : null;
this.messageConverters = messageConverters;
this.logger = logger;
}
前文說的泛型的返回值型別,這裡responseType
為 ParameterizedTypeImpl
型別
Debug發現
這裡Instaceof Class
是false
,則 this.responseClass = null
再看extractData
//HttpMessageConverterExtractor.java
public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
MediaType contentType = getContentType(responseWrapper);
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
......
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
if (messageConverter.canRead(this.responseClass, contentType)) {
......
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " +
"for response type [" + this.responseType + "] and content type [" + contentType + "]");
}
如果新增的是FastJsonHttpMessageConverter
,這裡他非 GenericHttpMessageConverter介面的實現類(注:可能是當前FastJson版本問題。),不是Spring自帶的,不是親兒子。
直接因為 this.responseClass = null
進入下面的內容,拋異常。
因為沒有一個HttpMessageConverter 是 CanRead這種型別的。
所以還是找他:MappingJackson2HttpMessageConverter
相關文章
- Springcloud(二) feignSpringGCCloud
- SpringCloud-FeignSpringGCCloud
- SpringCloud Feign的分析SpringGCCloud
- SpringCloud——Feign例項及原理SpringGCCloud
- 【SpringCloud】(八):認識Feign及使用SpringGCCloud
- 【SpringCloud】(九):Feign的自定義配置SpringGCCloud
- SpringCloud微服務治理二(Robbin,Hystix,Feign)SpringGCCloud微服務
- SpringCloud-使用Feign呼叫服務介面SpringGCCloud
- 【SpringCloud】(十四):Feign對Hystrix的支援 fallbackFactorySpringGCCloud
- springcloud中feign檔案上傳、下載SpringGCCloud
- (22)SpringCloud-使用Feign呼叫服務介面SpringGCCloud
- 分享一個 SpringCloud Feign 中所埋藏的坑SpringGCCloud
- SpringCloud 2020.0.4 系列之 FeignSpringGCCloud
- SpringCloud系列之使用Feign進行服務呼叫SpringGCCloud
- [jaeger] 四、微服務之呼叫鏈(Feign+SpringCloud)微服務SpringGCCloud
- SpringCloud之服務提供與呼叫(Ribbon,Feign)SpringGCCloud
- springcloud學習筆記(二)Spring Cloud FeignSpringGCCloud筆記
- SpringCloud之使用Feign跨服務呼叫最佳方式SpringGCCloud
- SpringCloud微服務(基於Eureka+Feign+Hystrix+Zuul)SpringGCCloud微服務Zuul
- SpringCloud學習筆記:宣告式呼叫Feign(4)SpringGCCloud筆記
- SpringCloud解決feign呼叫token丟失問題SpringGCCloud
- SpringCloud學習之路(四) - 服務消費者(Feign)SpringGCCloud
- ②SpringCloud 實戰:引入Feign元件,發起服務間呼叫SpringGCCloud元件
- SpringCloud 通過feign檔案傳輸並打zip包下載SpringGCCloud
- 微服務實戰SpringCloud之Spring Cloud Feign替代HTTP Client微服務SpringGCCloudHTTPclient
- SpringCloud基礎概念學習筆記(Eureka、Ribbon、Feign、Zuul)SpringGCCloud筆記Zuul
- 跟我學SpringCloud | 第三篇:服務的提供與Feign呼叫SpringGCCloud
- 【SpringCloud】(十三):全域性和單個禁用Feign Client對Hystrix的支援SpringGCCloudclient
- Encoder與Decoder
- GoCN每日新聞(2018-03-29)Go
- feign
- java B2B2C Springcloud電子商城系統-Feign負載均衡JavaSpringGCCloud負載
- 記一次線上SpringCloud-Feign請求服務超時異常排查SpringGCCloud
- 還在用Feign?推薦一款微服務間呼叫神器,跟SpringCloud絕配!微服務SpringGCCloud
- 企業分散式微服務雲SpringCloud SpringBoot mybatis - 服務消費者(Feign)分散式微服務GCCloudSpring BootMyBatis
- SpringCloud學習系列之二 ----- 服務消費者(Feign)和負載均衡(Ribbon)SpringGCCloud負載
- 白話SpringCloud | 第四章:服務消費者(RestTemple+Ribbon+Feign)SpringGCCloudREST
- 業餘草 SpringCloud教程 | 第三篇: 服務消費者(Feign)(Finchley版本)SpringGCCloud