使用feign傳送http請求解析報錯
錯誤如下:
傳送請求開始 ----- [ChannelFeign#formRecog] ---> END HTTP (304117-byte body) 傳送請求結束 返回開始 [ChannelFeign#formRecog] <--- HTTP/1.1 200 OK (4948ms) [ChannelFeign#formRecog] content-length: 5207 [ChannelFeign#formRecog] content-type: text/json;charset=UTF-8 [ChannelFeign#formRecog] date: Mon, 08 Oct 2018 10:47:03 GMT [ChannelFeign#formRecog] x-vcap-request-id: c323f65a-12e6-4604-7393-a4bf0ca403d5 [ChannelFeign#formRecog] [ChannelFeign#formRecog] {json格式的資料} [ChannelFeign#formRecog] <--- END HTTP (5207-byte body) 返回結束 ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.codec.DecodeException: Could not extract response: no suitable HttpMessageConverter found for response type [channel.domain.ChannelResponse<TableData>] and content type [text/json;charset=UTF-8]] with root cause org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [channel.domain.ChannelResponse<TableData>] and content type [text/json;charset=UTF-8] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:110) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.cloud.netflix.feign.support.SpringDecoder.decode(SpringDecoder.java:59) ~[spring-cloud-netflix-core-1.3.6.RELEASE.jar:1.3.6.RELEASE] at org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:47) ~[spring-cloud-netflix-core-1.3.6.RELEASE.jar:1.3.6.RELEASE] at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:165) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:133) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-9.5.0.jar:?] at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103) ~[feign-core-9.5.0.jar:?]
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [channel.domain.ChannelResponse<TableData>] and content type [text/json;charset=UTF-8]
可以看到返回的型別為[ChannelFeign#formRecog] content-type: text/json;charset=UTF-8
錯誤原因
介面返回為JSON格式資料但卻將資料表示為了[text/json]導致Feign沒有采用JSON解析器來解析,從而無法將響應資料轉化為對應的POJO物件;
原始碼分析
feign客戶端傳送請求入口函式invoke()
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("equals".equals(method.getName())) { try { Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null; return equals(otherHandler); } catch (IllegalArgumentException e) { return false; } } else if ("hashCode".equals(method.getName())) { return hashCode(); } else if ("toString".equals(method.getName())) { return toString(); } // 分發請求 return dispatch.get(method).invoke(args); }
Object decode(Response response) throws Throwable {
try {
return decoder.decode(response, metadata.returnType());
} catch (FeignException e) {
throw e;
} catch (RuntimeException e) {
throw new DecodeException(e.getMessage(), e);
}
}
進入decode.decode(),提取資料
@Override
@SuppressWarnings({"unchecked", "rawtypes", "resource"})
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)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + this.responseType + "] as \"" +
contentType + "\" using [" + messageConverter + "]");
}
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + this.responseClass.getName() + "] as \"" +
contentType + "\" using [" + messageConverter + "]");
}
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 + "]");
}
進入genericMessageConverter.canRead(this.responseType, null, contentType)
protected boolean canRead(MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
通過斷點發現mediaType為介面返回的content-type:text/json型別。而supportedMediaType為application/json,所以返回false,找不到合適的轉換器。
解決方案一
替代Feign的解碼器,使json解析器同時解析[text/plain]的資料
// 建立一個新的轉換器 解析微信的 [text/plain]
public class WxMessageConverter extends MappingJackson2HttpMessageConverter {
public WxMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_PLAIN);
setSupportedMediaTypes(mediaTypes);
}
}
注入新的Decoder Feign將自動 替換
// 解決微信返回引數為[text/plain] 無法轉化為json
@Bean
public Decoder feignDecoder(){
WxMessageConverter wxConverter = new WxMessageConverter();
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(wxConverter);
return new SpringDecoder(objectFactory);
}
參考:Buynow_Zhao部落格 https://blog.csdn.net/a610786189/article/details/80508353?utm_source=copy
解決方案二
對返回的json字串使用fastjosn轉換
String result = channelFeign.formRecogTest(channelRequest);
ChannelResponse<TableData> hello = JSONObject.parseObject(result,
new TypeReference<ChannelResponse<TableData>>() {
});
錯誤2
傳送請求時物件轉換json會自動將屬性的首字母小寫
解決方法:
//@Data
public class ChannelRequest {
//@JSONField(name="Header")
@JsonProperty
private ChannelReqHead Header;
//@JSONField(name="Body")
@JsonProperty
private ChannelReqBody Body;
// 如果get方法上不加JsonIgnore,jason化時小寫header也會出現
@JsonIgnore
public ChannelReqHead getHeader() {
return Header;
}
@JsonIgnore
public void setHeader(ChannelReqHead header) {
Header = header;
}
@JsonIgnore
public ChannelReqBody getBody() {
return Body;
}
@JsonIgnore
public void setBody(ChannelReqBody body) {
Body = body;
}
}
使用jsonField不起作用,
不使用jsonIgnore會生成大寫和小寫
如:{“Header”:xxx,"header":xxx}