I'm trying to implement some sort of proxy as part of my data flow, I want to receive a http-request on my inbound gateway and pass it through outbound gateway. I want preserve all query string parameters. My gateways configuration is:
<int:channel id="searchRequestChannel" />
<int:channel id="searchReplyChannel" />
<int-http:inbound-gateway id="searchRequestInboundGateway"
supported-methods="GET"
request-channel="searchRequestChannel"
reply-channel="searchReplyChannel"
path="/services/normalization"
reply-timeout="50000"
/>
<int-http:outbound-gateway id="searchServiceGateway"
http-method="GET"
request-channel="searchRequestChannel"
url="http://localhost:8080/query"
extract-request-payload="false"
expected-response-type="java.lang.String"
reply-timeout="50000"
charset="UTF-8"
/>
I expected that it would work as follows:
Client send request to the inbound gateway /services/normalization:
GET /services/normalization q=cat&exclude=black
Inbound gateway receives request and send it through searchRequestChannel to the outbound gateway.
Outbound gateway sends whole request to the external service:
GET /query q=cat&exclude=black
But on practice, outbound gateway sends empty request that does not contains any query arguments:
GET /query
So my question, what's easiest way to send the http-request that was accepted on inbound gateway through outbound gateway. In other words how can I implement simple proxy by spring integration tools?
This is a bit of a kludge, but works; the DispatcherServlet
binds the request to the thread...
<int-http:inbound-gateway id="searchRequestInboundGateway"
supported-methods="GET"
request-channel="searchRequestEnricherChannel"
reply-channel="searchReplyChannel"
path="/services/normalization{queryString}"
reply-timeout="50000"
/>
<int:header-enricher input-channel="searchRequestEnricherChannel" output-channel="searchRequestChannel">
<int:header name="queryString"
expression="T(org.springframework.web.context.request.RequestContextHolder).requestAttributes.request.queryString" />
</int:header-enricher>
and then on the outbound side, use
<int-http:outbound-gateway id="searchServiceGateway"
http-method="GET"
request-channel="searchRequestChannel"
url="http://localhost:8080/query?{queryString}"
encode-uri="false"
extract-request-payload="false"
expected-response-type="java.lang.String"
reply-timeout="50000"
charset="UTF-8">
<uri-variable name="queryString" expression="headers.queryString" />
</int-http:outbound-gateway>
However, this won't work with 2.2.x and earlier because the query string is encoded on the outbound side (foo=bar&baz=qux
becomes foo%3Dbar%26baz%3Dqux
). In 3.0 we have added the ability to not encode the URI using an attribute by using encode-uri="false"
. This is not yet available in a release, but it's available in 3.0.0.BUILD-SNAPSHOT
.
EDIT:
The above is a general solution that will work for all query strings; if you know the actual parameters, another solution would be to extract each parameter separately and rebuild the query string on the outbound side...
<int-http:inbound-gateway ... >
<int-http:header name="foo" expression="#requestParams.foo.get(0)"/>
<int-http:header name="baz" expression="#requestParams.baz.get(0)"/>
</int-http:inbound-gateway>
<int-http:outbound-gateway request-channel="requestChannel"
url="http://localhost:18080/http/receiveGateway?foo={foo}&baz={baz}"
http-method="POST"
expected-response-type="java.lang.String">
<int-http:uri-variable name="foo" expression="headers.foo"/>
<int-http:uri-variable name="baz" expression="headers.baz"/>
</int-http:outbound-gateway>
On the inbound side, it would be better if we offered the queryString as a first class expression variable #queryString
.
Please feel free to open an 'Improvement' JIRA Issue
My own workaround solution is use a transformer that transforms parameters in the message payload (map of query string parameters) to prepared query string and use an url-expression in an outbound-gateway to avoid a query string encoding:
<bean id="payloadToQueryString"
class="com.dph.integration.PayloadToQueryStringTransformer" />
<int-http:inbound-gateway id="searchRequestInboundGateway"
supported-methods="GET"
request-channel="searchRequestChannel"
path="/services/normalization"
reply-timeout="50000" />
<int:transformer input-channel="searchRequestChannel"
output-channel="searchGatewayChannel"
ref="payloadToQueryString" method="transform" />
<int-http:outbound-gateway id="searchServiceGateway"
http-method="GET"
request-channel="searchGatewayChannel"
url-expression="'http://localhost:8080/query?' + payload"
expected-response-type="java.lang.String"
reply-timeout="50000"
charset="UTF-8">
</int-http:outbound-gateway>
PayloadToQueryStringTransformer class is:
public class PayloadToQueryStringTransformer extends AbstractTransformer {
@Override
protected Object doTransform(final Message<?> message) throws Exception {
return MessageBuilder
.withPayload(urlEncodeUTF8(((MultiValueMap) message.getPayload()).toSingleValueMap()))
.copyHeaders(message.getHeaders())
.build();
}
private static String urlEncodeUTF8(final String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
private static String urlEncodeUTF8(final Map<?,?> map) {
final StringBuilder sb = new StringBuilder();
for (final Map.Entry<?,?> entry : map.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(String.format("%s=%s",
urlEncodeUTF8(entry.getKey().toString()),
urlEncodeUTF8(entry.getValue().toString())
));
}
return sb.toString();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With