spring 无法提取响应:找不到适合响应类型的 HttpMessageConverter

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24723394/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 06:49:44  来源:igfitidea点击:

Could not extract response: no suitable HttpMessageConverter found for response type

springspring-integration

提问by Anish Lodhi

I am new with spring integration and working in spring integration http module for my project requirement. I am sending request from outbound gateway as a http client. I am trying to initiate a request to the server and server should return me the message payload with my set values. I am converting object to JSON using to send to server I am sending a request to inbound gateway present on the server side from client(HttpClientDemo) shown below. For that purpose, I am converting my object into the JSON and then converting to JSON string to object on the client side, performing some simple operation there and sending it back to the client(HttpClientDemo) but before this, I am getting exception related to HttpMessageConverter as below:

我是 spring 集成的新手,并在 spring 集成 http 模块中为我的项目需求工作。我作为 http 客户端从出站网关发送请求。我正在尝试向服务器发起请求,服务器应该使用我的设置值返回消息负载。我正在将对象转换为 JSON 以发送到服务器 我正在从客户端(HttpClientDemo)向服务器端存在的入站网关发送请求,如下所示。为此,我将我的对象转换为 JSON,然后在客户端将对象转换为 JSON 字符串,在那里执行一些简单的操作并将其发送回客户端(HttpClientDemo),但在此之前,我遇到了与HttpMessageConverter 如下:

Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.mycompany.MyChannel.model.FFSampleResponseHttp] and content type [text/plain;charset=UTF-8]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:108)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:784)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:769)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:549)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:517)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:462)
    at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:421)
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:170)
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
    at org.springframework.integration.channel.AbstractSubscribablMyChannel.doSend(AbstractSubscribablMyChannel.java:77)
    at org.springframework.integration.channel.AbstractMessagMyChannel.send(AbstractMessagMyChannel.java:255)
    at org.springframework.integration.channel.AbstractMessagMyChannel.send(AbstractMessagMyChannel.java:223)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:114)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:44)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:93)

Please find Below related code :

请找到以下相关代码:

Client side code: HttpClientDemo.java

客户端代码:HttpClientDemo.java

public class HttpClientDemo {

    private static Logger logger = Logger.getLogger(HttpClientDemo.class);
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/http-outbound-config.xml");
RequestGateway requestGateway = context.getBean("requestGateway", RequestGateway.class);        
        FFSampleRequestHttp FFSampleRequesthttp = new FFSampleRequestHttp();
        FFSampleRequesthttp.setMyChannelID("1");
        FFSampleRequesthttp.setMyNumber("88");
        FFSampleRequesthttp.setReferenceID("9I");
        FFSampleRequesthttp.setTemplateType(1);
        FFSampleRequesthttp.setTimestamp("today");
        FFSampleResponseHttp  reply = requestGateway.FFSampleResponsegatway(FFSampleRequesthttp);
            logger.info("Replied with: " + reply);
    }

}

My Request Gateway is as follows: RequestGateway.java

我的请求网关如下:RequestGateway.java

public interface RequestGateway {


    FFSampleResponseHttp FFSampleResponsegatway(FFSampleRequestHttp request);

}

http-outbound-config.xml

http-outbound-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:int="http://www.springframework.org/schema/integration"
    xmlns:int-http="http://www.springframework.org/schema/integration/http"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
        http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">

    <int:gateway id="requestGateway" 
                 service-interface="com.mycompany.MyChannel.Common.RequestGateway"
                 default-request-channel="requestChannel"/>

    <int:channel id="requestChannel"/>
    <int:channel id="requestChannel1"/>


<!--    com.mycompany.MyChannel.model.FFSampleResponseHttp -->

    <int-http:outbound-gateway request-channel="requestChannel1" reply-channel="replyChannel1" url="http://localhost:8080/MyChannel_prj-1.0.0.BUILD-SNAPSHOT/receiveGateway"  http-method="POST"  extract-request-payload="true" expected-response-type="com.mycompany.MyChannel.model.FFSampleResponseHttp"/>

   <int:object-to-json-transformer  input-channel="requestChannel" output-channel="requestChannel1" content-type="application/json"  result-type="STRING"/>



   <bean id="FFSampleRequestHttp" class="com.mycompany.MyChannel.model.FFSampleRequestHttp"></bean>


</beans>

Web.xml

网页.xml

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


<servlet>
    <servlet-name>MyChannel-http</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/servlet-config.xml</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>MyChannel-http</servlet-name>
    <url-pattern>/receiveGateway</url-pattern>
   </servlet-mapping>


    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

</web-app>

servlet-config.xml

servlet-config.xml

    <int:channel id="receivMyChannel"/>

<int-http:inbound-gateway request-channel="receivMyChannel" path="/receiveGateway" supported-methods="POST"/>

    <int:service-activator input-channel="receivMyChannel">
        <bean class="com.mycompany.MyChannel.serviceImpl.FFSampleHttpImpl">
        <constructor-arg ref = "FFSampleRequestHttp"></constructor-arg>
        </bean>
        </int:service-activator>


     <bean id="FFSampleRequestHttp" class="com.mycompany.MyChannel.model.FFSampleRequestHttp"></bean>
     <bean id="FFSampleResponseHttp" class="com.mycompany.MyChannel.model.FFSampleResponseHttp"></bean>

    </beans> 



public class FFSampleHttpImpl{

    private static org.apache.log4j.Logger log = Logger
            .getLogger(FFSampleImpl.class);

    @Autowired
    FFSampleRequestHttp request;
    public FFSampleHttpImpl() {
    }

    public FFSampleHttpImpl(FFSampleRequestHttp request) {
        super();
        this.request = request;
    }





    public String issueResponseFor(String str) throws JsonParseException, JsonMappingException, IOException {

        ObjectMapper mapper = new ObjectMapper();

        FFSampleRequestHttp FFSampleRequestHttp = mapper.readValue(new String(str), FFSampleRequestHttp.class);

        FFSampleRequestHttp.setReferenceID("Hi My Number");

        String  strs = new String();

        strs = mapper.writeValueAsString(FFSampleRequestHttp);

            return strs;

        }

}

FFSampleRequestHttp.java

FFSampleRequestHttp.java

public class FFSampleRequestHttp {
    protected String MyNumber;  
    protected String referenceID;   
    protected String myChannelID;
    protected String timestamp;
    protected int templateType;
    public String getMyNumber() {
        return MyNumber;
    }
    public void setMyNumber(String MyNumber) {
        this.MyNumber = MyNumber;
    }
    public String getReferenceID() {
        return referenceID;
    }
    public void setReferenceID(String referenceID) {
        this.referenceID = referenceID;
    }
    public String getMyChannelID() {
        return myChannelID;
    }
    public void setMyChannelID(String myChannelID) {
        this.myChannelID = myChannelID;
    }
    public String getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
    public int getTemplateType() {
        return templateType;
    }
    public void setTemplateType(int templateType) {
        this.templateType = templateType;
    }
    }

FFSampleResponseHttp.java

FFSampleResponseHttp.java

public class FFSampleResponseHttp {
    protected String MyNumber;
    protected String referenceID;
    protected String myChannelID;
    protected String timestamp;
    protected int templateType;

    public String getMyNumber() {
        return MyNumber;
    }
    public void setMyNumber(String MyNumber) {
        this.MyNumber = MyNumber;
    }
    public String getReferenceID() {
        return referenceID;
    }
    public void setReferenceID(String referenceID) {
        this.referenceID = referenceID;
    }
    public String getMyChannelID() {
        return myChannelID;
    }
    public void setMyChannelID(String myChannelID) {
        this.myChannelID = myChannelID;
    }
    public String getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
    public int getTemplateType() {
        return templateType;
    }
    public void setTemplateType(int templateType) {
        this.templateType = templateType;
    }
    }

When I run the above code I get following error:

当我运行上面的代码时,我收到以下错误:

16:55:46.843 [main] DEBUG o.s.web.client.RestTemplate - Writing [{"MyNumber":"88","referenceID":"9I","myChannelID":"1","timestamp":"today","templateType":1}] as "text/plain;charset=UTF-8" using [org.springframework.http.converter.StringHttpMessageConverter@7d31a3e2]
16:55:46.988 [main] DEBUG o.s.web.client.RestTemplate - POST request for "http://localhost:8080/MyChannel_prj-1.0.0.BUILD-SNAPSHOT/receiveGateway" resulted in 200 (OK)
Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.mycompany.MyChannel.model.FFSampleResponseHttp] and content type [text/plain;charset=UTF-8]
    at org.springframework.web.client.HttpMessageConverterExtractor. 

I have used spring integration basic sample code for reference. Please provide your input. I also tried by using the spring object mapper in the configuration files with JSON to object transformer but then also I am getting similer issues for HttpMessageConverter. Please help me with your valuable inputs/suggestion and let me know if we have any limitation with spring integration http object mapper.

我已经使用了 spring 集成基本示例代码作为参考。请提供您的意见。我还尝试在配置文件中使用 spring 对象映射器和 JSON 到对象转换器,但是我也遇到了 HttpMessageConverter 的类似问题。请帮助我提供您宝贵的意见/建议,如果我们对 Spring 集成 http 对象映射器有任何限制,请告诉我。



Hi Artem, Thanks for your reply. I am still facing some challenges mentioned below. I have done the changes in my configuration files as per of your suggestion. but facing issue when using Hymanson2JsonObjectMapper and need your further help. Please find below issue description.

嗨阿尔乔姆,感谢您的回复。我仍然面临下面提到的一些挑战。我已经按照您的建议对我的配置文件进行了更改。但在使用 Hymanson2JsonObjectMapper 时遇到问题,需要您的进一步帮助。请在下面找到问题描述。

I have done changes in my files and now files are like below: My Servlet-Config.xml file content is as below:

我已经对我的文件进行了更改,现在文件如下:我的 Servlet-Config.xml 文件内容如下:

<int:channel id="channel1" /> 
<int:channel id="channel2" /> 
<int:channel id="channel3" /> 
<int-http:inbound-gateway request-channel="channel1" supported-methods="POST" path="/receiveGateway" /> 
- <int:service-activator input-channel="channel2"> 
- <bean class="com.myCompany.myChannel.serviceImpl.FFSampleHttpImpl"> 
<constructor-arg ref="ffSampleRequestHttp" /> 
</bean> 
</int:service-activator> 

<int:json-to-object-transformer input-channel="channel1" output-channel="channel2" type="com.myCompany.myChannel.model.FFSampleRequestHttp" object-mapper="Hymanson2JsonObjectMapper" /> 

<bean id="Hymanson2JsonObjectMapper" class="org.springframework.integration.support.json.Hymanson2JsonObjectMapper" /> 
<bean id="ffSampleRequestHttp" class="com.myCompany.myChannel.model.FFSampleRequestHttp" /> 
<bean id="ffSampleResponseHttp" class="com.myCompany.myChannel.model.FFSampleResponseHttp" /> 
</beans>

Out bound file config(file which is responsible to sent message to server):

出站文件配置(负责向服务器发送消息的文件):

<int:gateway id="requestGateway" service-interface="com.myCompany.myChannel.Common.RequestGateway" default-request-channel="requestChannel" /> 
  <int:channel id="requestChannel" /> 
  <int:channel id="requestChannel1" /> 
  <int:object-to-json-transformer input-channel="requestChannel" output-channel="requestChannel1" content-type="application/json" /> 
  <int-http:outbound-gateway request-channel="requestChannel1" reply-channel="channel4" url="http://localhost:8080/myChannel_prj-1.0.0.BUILD-SNAPSHOT/http/receiveGateway" http-method="POST" /> 
  <bean id="FFSampleRequestHttp" class="com.myCompany.myChannel.model.FFSampleRequestHttp" /> 
  <int:json-to-object-transformer input-channel="channel4" output-channel="requestChannel" type="com.myCompany.myChannel.model.FFSampleResponseHttp" object-mapper="Hymanson2JsonObjectMapper" /> 
  <bean id="Hymanson2JsonObjectMapper" class="org.springframework.integration.support.json.Hymanson2JsonObjectMapper" /> 
  </beans>

My impl class method is as below:

我的impl类方法如下:

public  FfSampleResponseHttp issueResponseFor(FfSampleRequestHttp request) {

        FfSampleResponseHttp ffSampleResponse2 = new FfSampleResponseHttp();

        ffSampleResponse2.setCifNumber("Yappi I am in the method");
        log.info("issueResponseFor(FfSampleRequesthttp request)");

        return ffSampleResponse2;

    }

I am able to call my service method issueResponseFor present in server side from the client but when this is processing further:

我可以从客户端调用我的服务方法 issueResponseFor 存在于服务器端,但是当这进一步处理时:

Caused by: java.lang.IllegalArgumentException: 'json' argument must be an instance of: [class java.lang.String, class [B, class java.io.File, class java.net.URL, class java.io.InputStream, class java.io.Reader]
       at org.springframework.integration.support.json.Hymanson2JsonObjectMapper.fromJson(Hymanson2JsonObjectMapper.java:93)
       at org.springframework.integration.support.json.Hymanson2JsonObjectMapper.fromJson(Hymanson2JsonObjectMapper.java:44)
       at org.springframework.integration.support.json.AbstractHymansonJsonObjectMapper.fromJson(AbstractHymansonJsonObjectMapper.java:55)
       at org.springframework.integration.json.JsonToObjectTransformer.doTransform(JsonToObjectTransformer.java:78)
       at org.springframework.integration.transformer.AbstractTransformer.transform(AbstractTransformer.java:33)
       ... 54 more

I have verified while debugging that the payload body while in response is coming blank in json object in the parameter of Hymanson2JsonObjectMapper.fromJson(…) after roaming through my service method successfully. I am not able to understand where am I doing the mistake. Please provide your help/input. Again let me know if I am again missing something in my config files. Thank you very much for your support.

我在调试时已经验证,在成功漫游我的服务方法后,响应的有效负载主体在 Hymanson2JsonObjectMapper.fromJson(...) 的参数中的 json 对象中变为空白。我无法理解我在哪里做错了。请提供您的帮助/意见。如果我的配置文件中再次丢失某些内容,请再次告诉我。非常感谢您的支持。

采纳答案by Artem Bilan

Since you return to the client just Stringand its content type == 'text/plain', there is no any chance for default converters to determine how to convert Stringresponse to the FFSampleResponseHttpobject.

由于您仅返回客户端String及其content type == 'text/plain',因此默认转换器没有任何机会确定如何将String响应转换为FFSampleResponseHttp对象。

The simple way to fix it:

修复它的简单方法:

  • remove expected-response-typefrom <int-http:outbound-gateway>
  • add to the replyChannel1<json-to-object-transformer>
  • 除去expected-response-type<int-http:outbound-gateway>
  • 添加到 replyChannel1<json-to-object-transformer>

Otherwise you should write your own HttpMessageConverterto convert the String to the appropriate object.

否则,您应该自己编写HttpMessageConverter将 String 转换为适当的对象。

To make it work with MappingHymanson2HttpMessageConverter(one of default converters) and your expected-response-type, you should send your reply with content type = 'application/json'.

要使其与MappingHymanson2HttpMessageConverter(默认转换器之一)和您的 一起使用expected-response-type,您应该使用content type = 'application/json'.

If there is a need, just add <header-enricher>after your <service-activator>and before sending a reply to the <int-http:inbound-gateway>.

如果有需要,只需<header-enricher>在您<service-activator>发送回复之前添加<int-http:inbound-gateway>

So, it's up to you which solution to select, but your current state doesn't work, because of inconsistency with default configuration.

因此,选择哪种解决方案取决于您,但由于与默认配置不一致,您当前的状态不起作用。

UPDATE

更新

OK. Since you changed your server to return FfSampleResponseHttpobject as HTTP response, not String, just add contentType = 'application/json'header before sending the response for the HTTP and MappingHymanson2HttpMessageConverterwill do the stuff for you - your object will be converted to JSON and with correct contentTypeheader.

好的。由于您更改了服务器以将FfSampleResponseHttp对象作为 HTTP 响应返回,而不是字符串,因此只需contentType = 'application/json'在发送HTTP 响应之前添加标头,并MappingHymanson2HttpMessageConverter为您完成这些操作 - 您的对象将被转换为 JSON 并带有正确的contentType标头。

From client side you should come back to the expected-response-type="com.mycompany.MyChannel.model.FFSampleResponseHttp"and MappingHymanson2HttpMessageConvertershould do the stuff for you again.

从客户端,您应该返回expected-response-type="com.mycompany.MyChannel.model.FFSampleResponseHttp"并且MappingHymanson2HttpMessageConverter应该再次为您做这些事情。

Of course you should remove <json-to-object-transformer>from you message flow after <int-http:outbound-gateway>.

当然,您应该<json-to-object-transformer><int-http:outbound-gateway>.

回答by Ivan Timoshin

As Artem Bilan said, this problem occures because MappingHymanson2HttpMessageConvertersupports response with application/json content-type only. If you can't change server code, but can change client code(I had such case), you can change content-type header with interceptor:

正如 Artem Bilan 所说,出现这个问题是因为仅MappingHymanson2HttpMessageConverter支持 application/json 内容类型的响应。如果您不能更改服务器代码,但可以更改客户端代码(我有这种情况),您可以使用拦截器更改内容类型标头:

restTemplate.getInterceptors().add((request, body, execution) -> {
            ClientHttpResponse response = execution.execute(request,body);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response;
        });

回答by ericdemo07

Here is a simple solution

这是一个简单的解决方案

try adding this dependency

尝试添加此依赖项

<dependency>
    <groupId>com.fasterxml.Hymanson.core</groupId>
    <artifactId>Hymanson-databind</artifactId>
    <version>2.8.3</version>
</dependency>

回答by Sushil

public class Application {

    private static List<HttpMessageConverter<?>> getMessageConverters() {
        List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
        converters.add(new MappingHymansonHttpMessageConverter());
        return converters;
    }   

    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();

        restTemplate.setMessageConverters(getMessageConverters());
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        //Page page = restTemplate.getForObject("http://graph.facebook.com/pivotalsoftware", Page.class);

        ResponseEntity<Page> response = 
                  restTemplate.exchange("http://graph.facebook.com/skbh86", HttpMethod.GET, entity, Page.class, "1");
        Page page = response.getBody();
        System.out.println("Name:    " + page.getId());
        System.out.println("About:   " + page.getFirst_name());
        System.out.println("Phone:   " + page.getLast_name());
        System.out.println("Website: " + page.getMiddle_name());
        System.out.println("Website: " + page.getName());
    }
}