使用feign发送http请求解析报错的问题

 更新时间:2022年03月16日 08:54:45   作者:yangchuanan  
这篇文章主要介绍了使用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);
    }

decode()返回请求的解码函数

  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);
}

解决方案二

对返回的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}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • EntityWrapper如何在and条件中嵌套or语句

    EntityWrapper如何在and条件中嵌套or语句

    这篇文章主要介绍了EntityWrapper如何在and条件中嵌套or语句,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • SpringBoot使用thymeleaf实现前端表格

    SpringBoot使用thymeleaf实现前端表格

    虽然现在流行前后端分离,但是后端模版在一些关键地方还是非常有用的,例如邮件模版、代码模版等。当然也不排除一些古老的项目后端依然使用动态模版。Thymeleaf 简洁漂亮、容易理解,并且完美支持 HTML5,可以直接打开静态页面,同时不新增标签,只需增强属性
    2022-10-10
  • RabbitMQ进阶之消息可靠性详解

    RabbitMQ进阶之消息可靠性详解

    这篇文章主要介绍了RabbitMQ进阶之消息可靠性详解,abbitmq消息的投递过程中,怎么确保消息能不丢失,这是一个很重要的问题,哪怕我们做了Rabbitmq持久化,也不能保证我们的业务消息不会被丢失,需要的朋友可以参考下
    2023-08-08
  • 关于在IDEA热部署插件JRebel使用问题详解

    关于在IDEA热部署插件JRebel使用问题详解

    这篇文章主要介绍了关于在IDEA热部署插件JRebel使用问题详解,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Spring中的@Repository注解源码详解

    Spring中的@Repository注解源码详解

    这篇文章主要介绍了Spring中的@Repository注解详解,@Repository注解修饰哪个类,则表明这个类具有对对象进行增删改查的功能,而且@Repository是@Component注解的一个派生品,所以被@Repository注解的类可以自动的被@ComponentScan通过路径扫描给找到,需要的朋友可以参考下
    2023-10-10
  • 利用idea搭建SSM项目看这一篇就够了

    利用idea搭建SSM项目看这一篇就够了

    SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容),常作为数据源较简单的web项目的框架,下面这篇文章主要给大家介绍了关于利用idea搭建SSM项目的相关资料,需要的朋友可以参考下
    2023-05-05
  • @Value如何设置默认值

    @Value如何设置默认值

    这篇文章主要介绍了@Value如何设置默认值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • 去掉IntelliJ IDEA 中 mybatis 对应的 xml 文件警告的教程图解

    去掉IntelliJ IDEA 中 mybatis 对应的 xml 文件警告的教程图解

    本文通过图文并茂的形式给大家介绍了去掉IntelliJ IDEA 中 mybatis 对应的 xml 文件警告的教程,需要的朋友可以参考下
    2018-06-06
  • 线程池满Thread pool exhausted排查和解决方案

    线程池满Thread pool exhausted排查和解决方案

    这篇文章主要介绍了线程池满Thread pool exhausted排查和解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • Java中的Semaphore源码分析

    Java中的Semaphore源码分析

    这篇文章主要介绍了Java中的Semaphore源码分析,Semaphore是一个访问公共资源的线程数量如限流、停车等,它是一个基于AQS实现的共享锁,主要是通过控制state变量来实现,需要的朋友可以参考下
    2023-11-11

最新评论