美文网首页
Feign踩坑记录:JSON parse error

Feign踩坑记录:JSON parse error

作者: 风雨兼程_ad4d | 来源:发表于2020-01-10 15:12 被阅读0次

    feign版本

    image

    feign调用时报错:

    feign.codec.DecodeException: Error while extracting response for type [class com.uaa.entity.resp.RespCompanyData] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens; nested exception is com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
    at [Source: (ByteArrayInputStream); line: 1, column: 2]

    解决方案

    1:pom文件添加feign-httpclient,将feign的http组件改为apache httpClient
    <dependency>
       <groupId>io.github.openfeign</groupId>
       <artifactId>feign-httpclient</artifactId>
    </dependency>
    
    2:检查配置文件是否开启了feign的gzip压缩
    feign:
     compression:
       request:
         enabled: true
       response:
         enabled: true
    
    如果找到该配置,请设置为false

    排查思路

    1.跟踪抛出异常的堆栈,发现在对返回结果的json解析中抛出异常


    image

    2.为什么会解析json失败呢,我们单独调用feign对应的接口是正常的,json也是正常可以解析的


    image

    3.难道feign的处理过返回的内容,又去跟了下fegin处理过程发现从response获取到流并没有任何异常,难道是出在了源头?但是源头又没有任何异常,此时思绪已经混乱,试着在google上查找有没有相关的问题,没想到在feign的github上找到类似问题https://github.com/OpenFeign/feign/issues/934

    image
    可以看到,feign默认的Client不支持设置了Content-Encoding为gzip的处理。看到此赶紧去看了下postman的Response Headers,果然发现了一行Content-Encoding:gzip!! image

    4.问题已然发现,就是响应的内容经过gzip编码,feign默认的Client不支持gzip解码。那么在此跟踪一下feign的源码查看处理过程,从入口SynchronousMethodHandler开始,在122行开始获取响应内容

    response = logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
    

    最终在Logger的102行找到响应流的读取,读取的流程如下:

    image 可以看出,使用的是HttpInputStream,并没有进行gzip解码的转换。我们可以手动进行gzip解码, image 此时可以看到解码后的内容正是我们预期的json!

    5.最终问题出在feign使用默认的HttpURLConnection,并没有经过任何处理,导致读取的是gzip压缩后的内容。此时我们可以将其置换为Httpclient,其内部ResponseContentEncodingprocess方法,取出了Content-Encoding并判断不为空,然后获取对应的处理方式。

    image 可以看到,如果Content-Encoding对应的value是gzip,那么就从decoderRegistry取出gzip对应的GZIPInputStream。至此真相大白!

    补充

    上面所说feign默认的Client不支持gzip解码可能容易引起歧义,应该是fegin默认的Client对响应流不支持对gzip后的字节流进行解析,所以在序列化成对象时会存在解析问题。如果一定要接收可以使用ResponseEntity<byte[]>来接收,这样feign就不会对其反序列化了。至于feign.compression.request.enabled=true,feign.compression.response.enabled=true配置的内容在FeignAcceptGzipEncodingInterceptor,FeignContentGzipEncodingInterceptor,大致可以看出只是在请求头添加了Header而已


    2020/3/13

    spring已添加支持,SpringCloud版升级到Hoxton即可

    https://github.com/spring-cloud/spring-cloud-openfeign/pull/230

    # 对于OkHttpClient以外的http客户端,可以启用默认的gzip解码器以UTF-8编码解码gzip响应
    feign.compression.response.enabled=true
    feign.compression.response.useGzipDecoder=true
    

    2020/12/01

    对于仍然存在问题的伙伴,可以直接使用OkHttp设置为feign的客户端(因为okhttp是默认支持gzip压缩),不需要关注spring cloud版本;最简单的方案,也是最推荐的方案

    • application.yml配置文件添加以下配置
    feign:
      okhttp:
        enabled: true
    
    • maven添加依赖
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-okhttp</artifactId>
    </dependency>
    

    相关文章

      网友评论

          本文标题:Feign踩坑记录:JSON parse error

          本文链接:https://www.haomeiwen.com/subject/nhzpactx.html