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

}

这样问题就解决了~

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。