2
votes

I'm trying to put together a really simple HTTP POST example using Spring Integration and a http outbound-gateway.
I need to be able to send a HTTP POST message with some POST parameters, as I would with curl:

$ curl -d 'fName=Fred&sName=Bloggs' http://localhost

I can get it working (without the POST parameters) if I send a simple String as the argument to the interface method, but I need to send a pojo, where each property of the pojo becomes a POST parameter.

I have the following SI config:

<?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="RequestGateway"
             default-request-channel="requestChannel"/>

    <int:channel id="requestChannel"/>

    <int-http:outbound-gateway request-channel="requestChannel"
                           url="http://localhost"
                           http-method="POST"
                           expected-response-type="java.lang.String"/>

</beans>

My RequestGateway interface looks like this:

public interface RequestGateway {
    String echo(Pojo request);
}

My Pojo class looks like this:

public class Pojo {
    private String fName;
    private String sName;

    public Pojo(String fName, String sName) {
        this.fName = fName;
        this.sName = sName;
    }

    .... getters and setters
}

And my class to kick it all off looks like this:

public class HttpClientDemo {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/si-email-context.xml");
        RequestGateway requestGateway = context.getBean("requestGateway", RequestGateway.class);

        Pojo pojo = new Pojo("Fred", "Bloggs");
        String reply = requestGateway.echo(pojo);
        System.out.println("Replied with: " + reply);
    }
}

When I run the above, I get:

org.springframework.web.client.RestClientException: Could not write request: no suitable HttpMessageConverter found for request type [Pojo] and content type [application/x-java-serialized-object]

I've googled a lot for this, but cannot find any examples of sending HTTP POST parameters with an outbound-gateway (I can find lots about setting HTTP Headers, but that's not what I'm trying to do here)
The only thing I did find was spring-integration: how to pass post request parameters to http-outbound but it's a slightly different use case as the OP was trying to send a JSON representation of his pojo which I am not, and the answer talks about setting headers, not POST parameters.

Any help with this would be very much appreciated;
Thanks
Nathan

1
Do you intend to use Java serialization for data transafer?jrao77
@jra077 - errr, not sure. Can you elaborate on your question please?Nathan Russell
In your curl example you have simple key, value pair which is being sent over HTTP. The same has been modelled as the class POJO when using spring integration. This class represents the data that has to be sent over HTTP. This can be done in several ways. We could use JSON, XML, multi-part form data, Java serialized objects etc. The error message states the content-type as application/x-java-serialized-object which means its trying to serialize the java object. If this is what is required then you have to set up the relevant message converters (both outgoing and on the service side).jrao77
Ah, understood. I now have it working by setting the Content-Type to multipart/form-data; and passing a Map into the RequestGateway interface method (rather than the Pojo); and then I have to write some code to populate the Map from the Pojo. This seems to work, but can I not simply send in the Pojo and have SI derive the key/value pairs ?Nathan Russell
Not sure if there is support for that. But you can write a custom converter which could be shared by all your requests and wired in directly to the gateway. Or you could used json which is handled automatically by SI. (Assuming the service is able to handle that)jrao77

1 Answers

2
votes

Thanks to the pointers from @jra077 regarding Content-Type, this is how I solved it.

My SI config now looks like this - the important bit was adding the Content-Type header:

<?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="RequestGateway"
             default-request-channel="requestChannel">
        <int:method name="sendConfirmationEmail">
            <int:header name="Content-Type" value="application/x-www-form-urlencoded"/>
        </int:method>
    </int:gateway>

    <int:channel id="requestChannel"/>

    <int-http:outbound-gateway request-channel="requestChannel"
                           url="http://localhost"
                           http-method="POST"
                           expected-response-type="java.lang.String"/>

</beans>

Then I changed my interface to take a Map as it's argument rather than the pojo:

public interface RequestGateway {
    String echo(Map<String, String> request);
}

The pojo itself remains as before; and the class that invokes the service is changed so that it creates a Map and passes it:

public class HttpClientDemo {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/si-email-context.xml");
        RequestGateway requestGateway = context.getBean("requestGateway", RequestGateway.class);

        Pojo pojo = new Pojo("Fred", "Bloggs");

        Map<String, String> requestMap = new HashMap<String, String>();
        requestMap.put("fName", pojo.getFName());
        requestMap.put("sName", pojo.getSName());

        String reply = requestGateway.echo(requestMap);
        System.out.println("Replied with: " + reply);
    }
}

I'm sure there are several more elegant ways of transforming the pojo into a Map, but for the time being this answers my question.

To send POST parameters with a http outbound-gateway you need to set the Content-Type to application/x-www-form-urlencoded and you need to pass a Map of key/values pairs.