springboot: no suitable HttpMessageConverter found for response

最近在使用 springboot 调用第三方接口的时候,出现了这个异常

Could not extract response: no suitable HttpMessageConverter found for response type [cn.justme.sboot.entity.BaseResp<java.lang.String>] and content type [text/plain;charset=UTF-8],

这里就来分析一下具体问题

事件还原

pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

feign调用

@FeignClient(name = "test", url = "http://localhost:8080")
public interface TestClient {
    @GetMapping("/api/test")
    BaseResp<String> test();
}

三方接口返回的是json,这里我是使用feign调用。

第三方接口

接口: /api/test

{
    "status": 0,
    "message": "",
    "data": null
}

我们可以看到 Content-Type: text/plain;charset=UTF-8 ,

原因分析

springboot默认的处理json的 HttpMessageConverterMappingJackson2HttpMessageConverter

public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
    super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}

但是 MappingJackson2HttpMessageConverter 只支持 APPLICATION_JSON 类型,所以springboot没有找到合适的 HttpMessageConverter ,于是报出了上面的异常。

feign默认的Decoder为 SpringDecoder

public class SpringDecoder implements Decoder {
    private ObjectFactory<HttpMessageConverters> messageConverters;
    public SpringDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        this.messageConverters = messageConverters;
    }
}

我们看一下默认的 messageConverters

解决方案

解决该问题,只要添加 MediaType.TEXT_PLAIN 类型的支持即可

方案一

仅仅在该 FeignClientMediaType.TEXT_PLAIN 的支持,不影响其他的client。

@FeignClient(name = "test", url = "http://localhost:8080", configuration = TestClient.FeignTestConfiguration.class)
public interface TestClient { 
    
    @GetMapping(“/api/test”)
    BaseResp test();
    
    class FeignTestConfiguration {
        @Bean
        public Decoder textPlainDecoder() {
            return new SpringDecoder(() -> new HttpMessageConverters(new CustomMappingJackson2HttpMessageConverter()));
        }
    }
    class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
        @Override
        public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
            super.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
        }
    }
}

调用接口,查看该FeignClientDecoder


此时调用成功。

方案二

全局修改,使所有的调用都支持TEXT_PLAINContent-Type

@Configuration
public class TestConfig {
    @Bean
    public MappingJackson2HttpMessageConverter customMappingJackson2HttpMessageConverter() {
        return new CustomMappingJackson2HttpMessageConverter();
    }
    static class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
        public CustomMappingJackson2HttpMessageConverter() {
            setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
        }
    }
}

方案三

我们也可以实现 WebMvcConfigurer 下面接口来实现对应功能。

/**
     * Configure the {@link HttpMessageConverter}s to use for reading or writing
     * to the body of the request or response. If no converters are added, a
     * default list of converters is registered.
     * <p><strong>Note</strong> that adding converters to the list, turns off
     * default converter registration. To simply add a converter without impacting
     * default registration, consider using the method
     * {@link #extendMessageConverters(java.util.List)} instead.
     * @param converters initially an empty list of converters
     */
    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }
    /**
     * A hook for extending or modifying the list of converters after it has been
     * configured. This may be useful for example to allow default converters to
     * be registered and then insert a custom converter through this method.
     * @param converters the list of configured converters to extend.
     * @since 4.1.3
     */
    default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    }


原文:https://justsme.github.io/2020/02/21/springboot-no-suitable-HttpMessageConverter-found-for-response/
作者:justsme