RestTemplate请求出现Could not extract response: no suitable HttpMessageConverter ...

1,033 阅读2分钟

在一次工作中,使用Springboot的RestTemplate请求接口时,出现了以下报错信息:

org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [text/plain;charset=UTF-8]

大概意思就是说:当前RestTemplate中的HttpMessageConverter转换器无法处理text/plain;charset=UTF-8类型,导致的结果就是RestTemplate将数据从HttpResponse转换成Object的时候,找不到合适的HttpMessageConverter进行转换!

问题查找

通过查看RestTemplate类源码,我们可以看到:

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
    private static final boolean romePresent;
    private static final boolean jaxb2Present;
    private static final boolean jackson2Present;
    private static final boolean jackson2XmlPresent;
    private static final boolean jackson2SmilePresent;
    private static final boolean jackson2CborPresent;
    private static final boolean gsonPresent;
    private static final boolean jsonbPresent;
    private final List<HttpMessageConverter<?>> messageConverters;
    private ResponseErrorHandler errorHandler;
    private UriTemplateHandler uriTemplateHandler;
    private final ResponseExtractor<HttpHeaders> headersExtractor;

    public RestTemplate() {
        this.messageConverters = new ArrayList();
        this.errorHandler = new DefaultResponseErrorHandler();
        this.headersExtractor = new HeadersExtractor();
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
        this.messageConverters.add(new ResourceHttpMessageConverter(false));


        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
        if (romePresent) {
            this.messageConverters.add(new AtomFeedHttpMessageConverter());
            this.messageConverters.add(new RssChannelHttpMessageConverter());
        }

        if (jackson2XmlPresent) {
            this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
        } else if (jaxb2Present) {
            this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
        }

        if (jackson2Present) {
            this.messageConverters.add(new MappingJackson2HttpMessageConverter());  // application/json
        } else if (gsonPresent) {
            this.messageConverters.add(new GsonHttpMessageConverter());
        } else if (jsonbPresent) {
            this.messageConverters.add(new JsonbHttpMessageConverter());
        }

        if (jackson2SmilePresent) {
            this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
        }

        if (jackson2CborPresent) {
            this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
        }

        this.uriTemplateHandler = initUriTemplateHandler();
    }

    public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
        this.messageConverters = new ArrayList();
        this.errorHandler = new DefaultResponseErrorHandler();
        this.headersExtractor = new HeadersExtractor();
        Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
        this.messageConverters.addAll(messageConverters);
        this.uriTemplateHandler = initUriTemplateHandler();
    }

    public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
        if (this.messageConverters != messageConverters) {
            this.messageConverters.clear();
            this.messageConverters.addAll(messageConverters);
        }

    }

    public List<HttpMessageConverter<?>> getMessageConverters() {
        return this.messageConverters;
    }
}

构造函数中包含了多种messageConverters消息转换器,其中RestTemplate使用MappingJackson2HttpMessageConverter默认处理application/json类型,但是我们发现确实没有其他的messageConverters支持处理text/plain,这样就会导致上述报错。

既然问题找到了,那么解决办法也很简单,就是再增加一个messageConverters,专门用于转换处理text/plain类型

解决

我们选择兼容的方式,而不是替换RestTemplate默认的消息转换器,即新建一个类,继承MappingJackson2HttpMessageConverter,如下:

/**
 * @date 2022-12-4
 * @desc 兼容处理 Content-Type 为 text/plain 类型的json返回值
 */
public class HsMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
    public HsMappingJackson2HttpMessageConverter() {
        List<MediaType> mediaTypes = new ArrayList<>();
        mediaTypes.add(MediaType.TEXT_PLAIN);
        setSupportedMediaTypes(mediaTypes);
    }
}

然后将这个消息转换器追加到RestTemplate中的 messageConverters即可,如下:

// 添加到 Springboot 的启动类中即可
@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getMessageConverters().add(new HsMappingJackson2HttpMessageConverter());  // 兼容 text/plain
    return restTemplate;
}

最后通过依赖注入的方式就可以正常处理,接口请求头中Content-Typetext/plain;charset=UTF-8的接口啦

@Resource
private final RestTemplate restTemplate;