如題:這篇文章主要講的是Spring Cloud Feign的Decoder
前提:使用FeignClient
并且返回值 使用泛型篡石,例如 PhpResponse<Model>。
SpringCloud版本:Brixton (Spring Core 4.2.3)
在HTTP協(xié)議不是很規(guī)范的情況下,需要配置Decoder
例如PHP寫的服務(wù)饰抒,就不跟你區(qū)分 ContentType 是不是JSON了坑鱼。即便是大廠騰訊咽白,相信他們的接口例如微信支付等等备埃,是不區(qū)分的节槐。
具體來說:就是返回數(shù)據(jù)是JSON,而ContentType 為 text/html;charset=UTF-8
這不影響你讀取他的文本內(nèi)容锯仪。
對于SpringCloud Feign來說:
在沒有寫Fallback的情況下:
Could not extract response: no suitable HttpMessageConverter found
如果寫了Fallback泵督,則即便返回了正常的JSON內(nèi)容,因為Decoder無法decode這種Response類型庶喜,則進(jìn)入了Fallback降級類了小腊。你看不到相關(guān)的錯誤日志,警告都沒有久窟。
問題現(xiàn)狀
如果是自己的公司秩冈,要求PHP傳 正確的ContentType ,不難斥扛,就是要說服他們改改入问。
也就一句話
header("ContentType", "application/json;charset=UTF-8");
而如果對方很調(diào)皮,表示怎么就你JAVA的要求這么高? Customer端也沒有要求這個稀颁?
而如果對方是騰訊的微信支付接口芬失,你讓人家改?
所以匾灶,還是要自己用一些方法處理的棱烂。
如果因此放棄Feign ,改用HTTPClient阶女,這也不甘心啊颊糜。
解決方案
貢獻(xiàn)出代碼:
@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")); //關(guān)鍵
setSupportedMediaTypes(mediaTypes);
}
}
}
這里關(guān)鍵位置,就添加了對這種Response的支持张肾。
RestTemplate的配置芭析,我還不會,這里就不寫了吞瞪。
思考
好像配置過FastJSON的MessageConverter的馁启?
下面的內(nèi)容將說明 我所用的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);
}
這里構(gòu)造了一個HttpMessageConverterExtractor芍秆,跟進(jìn)構(gòu)造函數(shù):
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發(fā)現(xiàn)
這里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接口的實現(xiàn)類(注:可能是當(dāng)前FastJson版本問題霉颠。),不是Spring自帶的荆虱,不是親兒子蒿偎。
直接因為 this.responseClass = null
進(jìn)入下面的內(nèi)容朽们,拋異常。
因為沒有一個HttpMessageConverter 是 CanRead這種類型的诉位。
所以還是找他:MappingJackson2HttpMessageConverter