feign.codec.DecodeException: Could not extract response: no suitable HttpMessageConverter found for
响叮当! 2024-09-13 15:35:01 阅读 67
一、报错
<code>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 格式。
但是<code>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
@Slf4j
public 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);
}
这样问题就解决了~
========================================================================
上一篇: JS常用的5个时间插件
下一篇: c语言-快速排序
本文标签
feign.codec.DecodeException: Could not extract response: no suitable HttpMessageConverter found for
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。