Merge pull request #755 from artembilan/INT-2455
* INT-2455: INT-2455: Support HTTP Outbound 'encode-uri' Flag
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,6 +32,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
|
||||
@@ -42,6 +43,8 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
|
||||
builder.addPropertyValue("expectReply", false);
|
||||
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
|
||||
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
|
||||
|
||||
String restTemplate = element.getAttribute("rest-template");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +31,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@@ -45,6 +46,8 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
|
||||
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
|
||||
|
||||
String restTemplate = element.getAttribute("rest-template");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,6 +62,8 @@ import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* A {@link MessageHandler} implementation that executes HTTP requests by delegating
|
||||
@@ -90,6 +92,8 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
|
||||
private final Expression uriExpression;
|
||||
|
||||
private volatile boolean encodeUri = true;
|
||||
|
||||
private volatile Expression httpMethodExpression = new LiteralExpression(HttpMethod.POST.name());
|
||||
|
||||
private volatile boolean expectReply = true;
|
||||
@@ -157,6 +161,16 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
this.evaluationContext = sec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the real URI should be encoded after <code>uriVariables</code>
|
||||
* expanding and before send request via {@link RestTemplate}. The default value is <code>true</code>.
|
||||
*
|
||||
* @see UriComponentsBuilder
|
||||
*/
|
||||
public void setEncodeUri(boolean encodeUri) {
|
||||
this.encodeUri = encodeUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the SpEL {@link Expression} to determine {@link HttpMethod} dynamically
|
||||
*
|
||||
@@ -348,7 +362,9 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
Class<?> expectedResponseType = this.determineExpectedResponseType(requestMessage);
|
||||
|
||||
HttpEntity<?> httpRequest = this.generateHttpRequest(requestMessage, httpMethod);
|
||||
ResponseEntity<?> httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, expectedResponseType, uriVariables);
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uri).buildAndExpand(uriVariables);
|
||||
URI realUri = this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString());
|
||||
ResponseEntity<?> httpResponse = this.restTemplate.exchange(realUri, httpMethod, httpRequest, expectedResponseType);
|
||||
if (this.expectReply) {
|
||||
HttpHeaders httpHeaders = httpResponse.getHeaders();
|
||||
Map<String, Object> headers = this.headerMapper.toHeaders(httpHeaders);
|
||||
|
||||
@@ -390,6 +390,15 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="encode-uri" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
When set to "false", the real URI won't be encoded before the request is sent. This may be useful
|
||||
in some scenarios as it allows user control over the encoding, if needed,
|
||||
for example by using the "url-expression". Default is "true".
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="http-method">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -560,6 +569,15 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="encode-uri" type="xsd:string" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
When set to "false", the real URI won't be encoded before the request is sent. This may be useful
|
||||
in some scenarios as it allows user control over the encoding, if needed,
|
||||
for example by using the "url-expression". Default is "true".
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="http-method">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
@@ -9,15 +9,22 @@
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<si:chain input-channel="httpOutboundChannelAdapterWithinChain">
|
||||
<outbound-channel-adapter url="http://localhost/test1" rest-template="restTemplate"/>
|
||||
<outbound-channel-adapter url="http://localhost/test1/%2f" encode-uri="false" rest-template="restTemplate"/>
|
||||
</si:chain>
|
||||
|
||||
<beans:bean id="restTemplate" class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests$MockRestTemplate2"/>
|
||||
<beans:bean id="restTemplate" class="org.mockito.Mockito" factory-method="spy">
|
||||
<beans:constructor-arg>
|
||||
<beans:bean class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests$MockRestTemplate2"/>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
|
||||
<si:chain input-channel="httpOutboundGatewayWithinChain" output-channel="replyChannel">
|
||||
<outbound-gateway url="http://localhost:51235/testApps/httpOutboundGatewayWithinChain"
|
||||
<outbound-gateway url="http://localhost:51235/%2f/testApps?param={param}"
|
||||
rest-template="restTemplate"
|
||||
expected-response-type="java.lang.String"/>
|
||||
encode-uri="false"
|
||||
expected-response-type="java.lang.String">
|
||||
<uri-variable name="param" expression="T(org.apache.commons.httpclient.util.URIUtil).encodeWithinQuery('http Outbound Gateway Within Chain')"/>
|
||||
</outbound-gateway>
|
||||
</si:chain>
|
||||
|
||||
<si:channel id="replyChannel">
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -42,6 +43,7 @@ import javax.xml.transform.Source;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
@@ -673,23 +675,28 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
}
|
||||
|
||||
@Test //INT-2275
|
||||
public void testOutboundChannelAdapterWithinChain() {
|
||||
public void testOutboundChannelAdapterWithinChain() throws URISyntaxException {
|
||||
ApplicationContext ctx = new ClassPathXmlApplicationContext("HttpOutboundWithinChainTests-context.xml", this.getClass());
|
||||
MessageChannel channel = ctx.getBean("httpOutboundChannelAdapterWithinChain", MessageChannel.class);
|
||||
RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class);
|
||||
channel.send(MessageBuilder.withPayload("test").build());
|
||||
// It's just enough if it was sent successfully from chain without any failures
|
||||
Mockito.verify(restTemplate).exchange(Mockito.eq(new URI("http://localhost/test1/%2f")), Mockito.eq(HttpMethod.POST),
|
||||
Mockito.any(HttpEntity.class), Mockito.eq((Class<?>) null));
|
||||
}
|
||||
|
||||
@Test //INT-1029
|
||||
public void testHttpOutboundGatewayWithinChain() throws IOException {
|
||||
public void testHttpOutboundGatewayWithinChain() throws IOException, URISyntaxException {
|
||||
ApplicationContext ctx = new ClassPathXmlApplicationContext("HttpOutboundWithinChainTests-context.xml", this.getClass());
|
||||
MessageChannel channel = ctx.getBean("httpOutboundGatewayWithinChain", MessageChannel.class);
|
||||
RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class);
|
||||
channel.send(MessageBuilder.withPayload("test").build());
|
||||
|
||||
PollableChannel output = ctx.getBean("replyChannel", PollableChannel.class);
|
||||
Message<?> receive = output.receive();
|
||||
assertEquals(HttpStatus.OK, ((ResponseEntity<?>)receive.getPayload()).getStatusCode());
|
||||
|
||||
Mockito.verify(restTemplate)
|
||||
.exchange(Mockito.eq(new URI("http://localhost:51235/%2f/testApps?param=http%20Outbound%20Gateway%20Within%20Chain")),
|
||||
Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.eq(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -698,7 +705,7 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(
|
||||
new SpelExpressionParser().parseExpression("headers['foo']"),
|
||||
restTemplate);
|
||||
String theURL = "http://bar/baz";
|
||||
String theURL = "http://bar/baz?foo#bar";
|
||||
Message<?> message = MessageBuilder.withPayload("").setHeader("foo", theURL).build();
|
||||
try {
|
||||
handler.handleRequestMessage(message);
|
||||
@@ -707,6 +714,21 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
assertEquals(theURL, restTemplate.actualUrl.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt2455UriNotEncoded() {
|
||||
MockRestTemplate restTemplate = new MockRestTemplate();
|
||||
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(
|
||||
new SpelExpressionParser().parseExpression("'http://my.RabbitMQ.com/api/' + payload"),
|
||||
restTemplate);
|
||||
handler.setEncodeUri(false);
|
||||
Message<?> message = MessageBuilder.withPayload("queues/%2f/si.test.queue?foo#bar").build();
|
||||
try {
|
||||
handler.handleRequestMessage(message);
|
||||
}
|
||||
catch (Exception e) {}
|
||||
assertEquals("http://my.RabbitMQ.com/api/queues/%2f/si.test.queue?foo#bar", restTemplate.actualUrl.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonCompatibleConversionService() throws Exception {
|
||||
HttpRequestExecutingMessageHandler handler =
|
||||
@@ -857,9 +879,9 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
private final AtomicReference<String> actualUrl = new AtomicReference<String>();
|
||||
|
||||
@Override
|
||||
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
|
||||
this.actualUrl.set(url);
|
||||
public <T> ResponseEntity<T> exchange(URI uri, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
Class<T> responseType) throws RestClientException {
|
||||
this.actualUrl.set(uri.toString());
|
||||
this.lastRequestEntity.set(requestEntity);
|
||||
throw new RuntimeException("intentional");
|
||||
}
|
||||
@@ -869,8 +891,8 @@ public class HttpRequestExecutingMessageHandlerTests {
|
||||
private static class MockRestTemplate2 extends RestTemplate {
|
||||
|
||||
@Override
|
||||
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
|
||||
public <T> ResponseEntity<T> exchange(URI uri, HttpMethod method, HttpEntity<?> requestEntity,
|
||||
Class<T> responseType) throws RestClientException {
|
||||
return new ResponseEntity<T>(HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
</para>
|
||||
</note>
|
||||
<para>
|
||||
<emphasis>Mapping URI variables</emphasis>
|
||||
<emphasis>Mapping URI Variables</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
If your URL contains URI variables, you can map them using the
|
||||
@@ -402,6 +402,24 @@ In the case of the Outbound Gateway, the reply message produced by the gateway w
|
||||
method will be invoked on the payload object of the Message and the result
|
||||
of that method will be used as the value for the URI variable named 'zipCode'.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Controlling URI Encoding</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
By default, the URL string is encoded (see
|
||||
<ulink url="http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html">UriComponentsBuilder</ulink>)
|
||||
to the URI object before sending the request. In some scenarios with a non-standard URI (e.g. the RabbitMQ Rest API) it is
|
||||
undesirable to perform the encoding. The <code><http:outbound-gateway/></code>
|
||||
and <code><http:outbound-channel-adapter/></code> provide an <code>encode-uri</code> attribute.
|
||||
To disable encoding the URL, this attribute should be set to <code>false</code> (by default it is <code>true</code>).
|
||||
If you wish to partially encode some of the URL, this can be achieved using an <code>expression</code> within a
|
||||
<code><uri-variable/></code>:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<http:outbound-gateway url="http://somehost/%2f/fooApps?bar={param}" encode-uri="false">
|
||||
<http:uri-variable name="param"
|
||||
expression="T(org.apache.commons.httpclient.util.URIUtil)
|
||||
.encodeWithinQuery('Hellow World!')"/>
|
||||
</http:outbound-gateway>]]></programlisting>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -136,5 +136,14 @@
|
||||
see 'JSON Transformers' in <xref linkend="transformer"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-http-encode-uri">
|
||||
<title>HTTP Outbound Endpoint 'encode-uri' property</title>
|
||||
<para>
|
||||
<code><http:outbound-gateway/></code> and <code><http:outbound-channel-adapter/></code> now
|
||||
provide an <code>encode-uri</code> attribute to allow disabling the encoding of the URI object
|
||||
before sending the request. For more information see <xref linkend="http"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user