当前位置:首页 » 《我的小黑屋》 » 正文

feign.codec.DecodeException: Could not extract response: no suitable HttpMessageConverter found for

12 人参与  2024年09月20日 10:01  分类 : 《我的小黑屋》  评论

点击全文阅读


一、报错

Could not extract response: no suitable HttpMessageConverter found for response type [xxx] and content type [application/octet-stream] feign.codec.DecodeException: Could not extract response: no suitable HttpMessageConverter found for response type [xxx] and content type [application/octet-stream]

解释一下:就是Feign自带的转换器中,无法把[class XXX]转换成content type [XXX;XXX]

在使用SpringCloud FeignClient的时候,经常的情况是我们希望返回的是一个application/json类型的返回,当返回是[application/octet-stream]类型或者其他自带转换器中没有的类型时,feign调用就会解析错误。

二、具体源码分析

(1)在 Springboot 默认处理器为 MappingJackson2HttpMessageConverter

用于将 Java 对象转换为 JSON 格式。

但是MappingJackson2HttpMessageConverter,只支持"application/json"类型;因此springboot没有找到合适的HttpMessageConverter,于是报出了上面的异常。

 (2)feign默认的Decoder为SpringDecoder

SpringDecoder的decode的过程:

判断目标类型合法的利用Spring框架的消息转换器messageConverters来执行这个任务,并可以通过自定义器来customizers自定义转换器的行为创建一个HttpMessageConverterExtractor对象,最终通过调用该对象的extractData方法将Feign的响应Response对象转化为Java对象

二、解决措施

解决该问题,可以自定义一个Feign的Decoder,只要添加MediaType.APPLICATION_OCTET_STREAM_VALUE类型的支持即可。

第一步:自定义一个Decoder:

import com.fasterxml.jackson.databind.ObjectMapper;import feign.Response;import feign.codec.Decoder;import lombok.extern.slf4j.Slf4j;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;import org.springframework.util.StreamUtils;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Type;import java.nio.charset.StandardCharsets;import java.util.Collection;import java.util.Collections;@Configuration@Slf4jpublic class FeignConfiguration {    @Bean    public Decoder feignDecoder() {        return new CustomFeignDecoder();    }    public class CustomFeignDecoder implements Decoder {        private final ObjectMapper objectMapper = new ObjectMapper();        private final HttpMessageConverter<Object> messageConverter = new MappingJackson2HttpMessageConverter(objectMapper);        @Override        public Object decode(Response response, Type type) throws IOException {            if (response.status() == HttpStatus.NO_CONTENT.value() || response.body() == null) {                return null;            }            Collection<String> contentTypeHeader = response.headers().getOrDefault("Content-Type", Collections.singletonList(MediaType.APPLICATION_OCTET_STREAM_VALUE));            log.info("Response headers:{}", response.headers());            String contentType;            if (contentTypeHeader != null && !contentTypeHeader.isEmpty()) {                contentType = contentTypeHeader.iterator().next();            } else {                contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;            }            MediaType mediaType = MediaType.parseMediaType(contentType);            String body = StreamUtils.copyToString(response.body().asInputStream(), StandardCharsets.UTF_8);            return messageConverter.read((Class<?>) type, new FakeHttpInputMessage(body, mediaType));        }    }    public static class FakeHttpInputMessage implements org.springframework.http.HttpInputMessage {        private final String body;        private final MediaType mediaType;        public FakeHttpInputMessage(String body, MediaType mediaType) {            this.body = body;            this.mediaType = mediaType;        }        @Override        public InputStream getBody() throws IOException {            return new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));        }        @Override        public org.springframework.http.HttpHeaders getHeaders() {            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();            headers.setContentType(mediaType);            return headers;        }    }}
 

首先获取默认的Content-Type值,如果没有,就返回一个MediaType.APPLICATION_OCTET_STREAM_VALUE类型的。

第二步:在Feign接口上加上:

configuration = FeignConfiguration.class

import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestHeader;@FeignClient(url = "${xxx.url:}", name = "TestFeignApi", configuration = FeignConfiguration.class)public interface TestFeignApi {    @PostMapping(path = "/aaa/bbb/checkUserTicket")    UserResponse checkUserTicket(@RequestBody UserReq req,                                 @RequestHeader("x-token") String token,                                 @RequestHeader("x-id") String id);}

这样问题就解决了~

========================================================================


点击全文阅读


本文链接:http://zhangshiyu.com/post/162050.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1