在使用SpringCloud FeignClient的时候,经常的情况是我们希望返回的是一个application/json类型的返回,但是有时候我们会碰到如下的错误
Could not extract response: no suitable HttpMessageConverter found for response type and content type [application/octet-stream]
开启调试,跟踪代码到HttpMessageConverterExtractor类,关键方法如下
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 + "]");
}
远程接口由于各种问题没有在响应中设置content-type Header,导致FeignClient接收的时候contentType为null,HttpMessageConverterExtractor将其设置为MediaType.APPLICATION_OCTET_STREAM
private MediaType getContentType(ClientHttpResponse response) {
MediaType contentType = response.getHeaders().getContentType();
if (contentType == null) {
if (logger.isTraceEnabled()) {
logger.trace("No Content-Type header found, defaulting to application/octet-stream");
}
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
return contentType;
}
但是this.messageConverters中的messageConverter中可以处理json类型响应的
解决方案就是配置Decoder,增加一个HttpMessageConverter,让其支持MediaType.APPLICATION_OCTET_STREAM
@Component
@FeignClient(url = "${host}", configuration = {MyFeignClient.Config.class})
interface MyFeignClient {
@PostMapping("/call")
ResponseEntity<Map<String, Object>> doCall(@RequestBody Map<String, String> param)
@Slf4j
static class Config{
@Bean
Decoder feignDecoder() {
HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper())
List<MediaType> unmodifiedMediaTypeList = jacksonConverter.getSupportedMediaTypes()
// 因为上条语句得到的是unmodified List类型,所以需要用另一个List进行操作
List<MediaType> mediaTypeList = new ArrayList<>(unmodifiedMediaTypeList.size() + 1)
mediaTypeList.addAll(unmodifiedMediaTypeList)
mediaTypeList.add(MediaType.APPLICATION_OCTET_STREAM)
jacksonConverter.setSupportedMediaTypes(mediaTypeList)
ObjectFactory<HttpMessageConverters> objectFactory = new ObjectFactory<HttpMessageConverters>() {
@Override
HttpMessageConverters getObject() throws BeansException {
return new HttpMessageConverters(jacksonConverter)
}
}
return new ResponseEntityDecoder(new SpringDecoder(objectFactory))
}
private static ObjectMapper customObjectMapper(){
ObjectMapper objectMapper = new ObjectMapper()
return objectMapper
}
}
}
网友评论