GH-3154: Support UriBuilderFactory.EncodingMode (#3162)

* GH-3154: Support `UriBuilderFactory.EncodingMode`

Fixes https://github.com/spring-projects/spring-integration/issues/3154

Spring Framework now provides a `DefaultUriBuilderFactory.EncodingMode`
for encoding URIs in the `RestTemplate` before and after uri template
enrichment with uri variables.
Therefore `encodeUri` and manual uri variables substitution is not necessary
in Spring Integration HTTP components

* Deprecate `AbstractHttpRequestExecutingMessageHandler.encodeUri` in favor of
`DefaultUriBuilderFactory.EncodingMode` and respective configuration
on the `RestTemplate` in HTTP module and `WebClient` in WebFlux module

* * Really populate `uriFactory` into an internal `RestTemplate`
* Ensure in tests that `encoding-mode` is populated properly into an internal `RestTemplate`
* Clean up affected HTTP tests for AssertJ and JUnit 5

* * Clean up formatting

* * Apply fix for WebFlux module
* Add docs for new `encoding-mode` option

* * Remove unused import in the test
This commit is contained in:
Artem Bilan
2020-01-30 15:12:39 -05:00
committed by GitHub
parent 9f07803abf
commit 89d86e1904
17 changed files with 420 additions and 340 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.util.StringUtils;
/**
@@ -46,7 +47,6 @@ 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 headerMapper = element.getAttribute("header-mapper");
@@ -60,8 +60,8 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
builder.addPropertyReference("headerMapper", headerMapper);
}
else if (StringUtils.hasText(mappedRequestHeaders)) {
BeanDefinitionBuilder headerMapperBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.http.support.DefaultHttpHeaderMapper");
BeanDefinitionBuilder headerMapperBuilder =
BeanDefinitionBuilder.genericBeanDefinition(DefaultHttpHeaderMapper.class);
IntegrationNamespaceUtils.setValueIfAttributeDefined(headerMapperBuilder, element,
"mapped-request-headers", "outboundHeaderNames");
builder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition());
@@ -90,6 +90,8 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode");
}
return builder;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -47,7 +47,6 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
BeanDefinitionBuilder builder = getBuilder(element, parserContext);
HttpAdapterParsingUtils.configureUrlConstructorArg(element, parserContext, builder);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
HttpAdapterParsingUtils.setHttpMethodOrExpression(element, parserContext, builder);
String headerMapper = element.getAttribute("header-mapper");
@@ -103,6 +102,8 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode");
}
return builder;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 the original author or authors.
* Copyright 2017-2020 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.
@@ -34,6 +34,7 @@ import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* The base {@link MessageHandlerSpec} for {@link AbstractHttpRequestExecutingMessageHandler}s.
@@ -71,12 +72,25 @@ public abstract class BaseHttpMessageHandlerSpec<S extends BaseHttpMessageHandle
* expanding and before send request via underlying implementation. The default value is <code>true</code>.
* @param encodeUri true if the URI should be encoded.
* @return the spec
* @deprecated since 5.3 in favor of {@link #encodingMode}
*/
@Deprecated
public S encodeUri(boolean encodeUri) {
this.target.setEncodeUri(encodeUri);
return _this();
}
/**
* Specify a {@link DefaultUriBuilderFactory.EncodingMode} for uri construction.
* @param encodingMode to use for uri construction.
* @return the spec
* @since 5.3
*/
public S encodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) {
this.target.setEncodingMode(encodingMode);
return _this();
}
/**
* Specify the SpEL {@link Expression} to determine {@link HttpMethod} at runtime.
* @param httpMethodExpression The method expression.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 the original author or authors.
* Copyright 2017-2020 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.
@@ -17,7 +17,6 @@
package org.springframework.integration.http.outbound;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -27,7 +26,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Supplier;
import javax.xml.transform.Source;
@@ -56,14 +54,13 @@ import org.springframework.integration.support.AbstractIntegrationMessageBuilder
import org.springframework.integration.support.MessageBuilderFactory;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriComponentsBuilder;
/**
@@ -85,6 +82,8 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
private static final List<HttpMethod> NO_BODY_HTTP_METHODS =
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE);
protected final DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory(); // NOSONAR - final
private final Map<String, Expression> uriVariableExpressions = new HashMap<>();
private final Expression uriExpression;
@@ -95,8 +94,6 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
private boolean trustedSpel;
private boolean encodeUri = true;
private Expression httpMethodExpression = new ValueExpression<>(HttpMethod.POST);
private boolean expectReply = true;
@@ -127,9 +124,27 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
* <code>true</code>.
* @param encodeUri true if the URI should be encoded.
* @see UriComponentsBuilder
* @deprecated since 5.3 in favor of {@link #setEncodingMode}
*/
@Deprecated
public void setEncodeUri(boolean encodeUri) {
this.encodeUri = encodeUri;
setEncodingMode(
encodeUri
? DefaultUriBuilderFactory.EncodingMode.TEMPLATE_AND_VALUES
: DefaultUriBuilderFactory.EncodingMode.NONE);
}
/**
* Set the encoding mode to use.
* By default this is set to {@link DefaultUriBuilderFactory.EncodingMode#TEMPLATE_AND_VALUES}.
* For more complicated scenarios consider to configure an {@link org.springframework.web.util.UriTemplateHandler}
* on an externally provided {@link org.springframework.web.client.RestTemplate}.
* @param encodingMode the mode to use for uri encoding
* @since 5.3
*/
public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) {
Assert.notNull(encodingMode, "'encodingMode' must not be null");
this.uriFactory.setEncodingMode(encodingMode);
}
/**
@@ -291,32 +306,23 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
Object expectedResponseType = determineExpectedResponseType(requestMessage);
HttpEntity<?> httpRequest = generateHttpRequest(requestMessage, httpMethod);
return exchange(() -> generateUri(requestMessage), httpMethod, httpRequest, expectedResponseType,
requestMessage);
}
protected abstract Object exchange(Supplier<URI> uriSupplier, HttpMethod httpMethod, HttpEntity<?> httpRequest,
Object expectedResponseType, Message<?> requestMessage);
private URI generateUri(Message<?> requestMessage) {
Object uri = this.uriExpression.getValue(this.evaluationContext, requestMessage);
Assert.state(uri instanceof String || uri instanceof URI,
() -> "'uriExpression' evaluation must result in a 'String' or 'URI' instance, not: "
+ (uri == null ? "null" : uri.getClass()));
Map<String, ?> uriVariables = determineUriVariables(requestMessage);
UriComponentsBuilder uriComponentsBuilder =
uri instanceof String
? UriComponentsBuilder.fromUriString((String) uri)
: UriComponentsBuilder.fromUri((URI) uri);
UriComponents uriComponents = uriComponentsBuilder.buildAndExpand(uriVariables);
try {
return this.encodeUri ? uriComponents.encode().toUri() : new URI(uriComponents.toUriString());
}
catch (URISyntaxException e) {
throw new MessageHandlingException(requestMessage, "Invalid URI [" + uri + "] in the [" + this + ']', e);
Map<String, ?> uriVariables = null;
if (uri instanceof String) {
uriVariables = determineUriVariables(requestMessage);
}
return exchange(uri, httpMethod, httpRequest, expectedResponseType, requestMessage, uriVariables);
}
protected abstract Object exchange(Object uri, HttpMethod httpMethod, HttpEntity<?> httpRequest,
Object expectedResponseType, Message<?> requestMessage, Map<String, ?> uriVariables);
protected Object getReply(ResponseEntity<?> httpResponse) {
if (this.expectReply) {
HttpHeaders httpHeaders = httpResponse.getHeaders();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -18,7 +18,7 @@ package org.springframework.integration.http.outbound;
import java.net.URI;
import java.util.List;
import java.util.function.Supplier;
import java.util.Map;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.expression.Expression;
@@ -29,12 +29,14 @@ import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.integration.expression.ValueExpression;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.util.Assert;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* A {@link org.springframework.messaging.MessageHandler}
@@ -64,6 +66,8 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
private final RestTemplate restTemplate;
private final boolean restTemplateExplicitlySet;
/**
* Create a handler that will send requests to the provided URI.
* @param uri The URI.
@@ -109,14 +113,24 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
* {@link org.springframework.beans.factory.BeanFactory}.
* @param restTemplate The rest template.
*/
public HttpRequestExecutingMessageHandler(Expression uriExpression, RestTemplate restTemplate) {
public HttpRequestExecutingMessageHandler(Expression uriExpression, @Nullable RestTemplate restTemplate) {
super(uriExpression);
this.restTemplate = (restTemplate == null ? new RestTemplate() : restTemplate);
this.restTemplateExplicitlySet = restTemplate != null;
this.restTemplate = (this.restTemplateExplicitlySet ? restTemplate : new RestTemplate());
if (!this.restTemplateExplicitlySet) {
this.restTemplate.setUriTemplateHandler(this.uriFactory);
}
}
@Override
public String getComponentType() {
return (this.isExpectReply() ? "http:outbound-gateway" : "http:outbound-channel-adapter");
return (isExpectReply() ? "http:outbound-gateway" : "http:outbound-channel-adapter");
}
private void assertLocalRestTemplate(String option) {
Assert.isTrue(!this.restTemplateExplicitlySet,
() -> "The option '" + option + "' must be provided on the externally configured RestTemplate: "
+ this.restTemplate);
}
/**
@@ -125,6 +139,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
* @see RestTemplate#setErrorHandler(ResponseErrorHandler)
*/
public void setErrorHandler(ResponseErrorHandler errorHandler) {
assertLocalRestTemplate("errorHandler");
this.restTemplate.setErrorHandler(errorHandler);
}
@@ -135,6 +150,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
* @see RestTemplate#setMessageConverters(java.util.List)
*/
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
assertLocalRestTemplate("messageConverters");
this.restTemplate.setMessageConverters(messageConverters);
}
@@ -144,24 +160,43 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu
* @see RestTemplate#setRequestFactory(ClientHttpRequestFactory)
*/
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
assertLocalRestTemplate("requestFactory");
this.restTemplate.setRequestFactory(requestFactory);
}
@Override
protected Object exchange(Supplier<URI> uriSupplier, HttpMethod httpMethod, HttpEntity<?> httpRequest,
Object expectedResponseType, Message<?> requestMessage) {
public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) {
assertLocalRestTemplate("encodingMode on UriTemplateHandler");
super.setEncodingMode(encodingMode);
}
@Override
protected Object exchange(Object uri, HttpMethod httpMethod, HttpEntity<?> httpRequest,
Object expectedResponseType, Message<?> requestMessage, Map<String, ?> uriVariables) {
URI uri = uriSupplier.get();
ResponseEntity<?> httpResponse;
try {
if (expectedResponseType instanceof ParameterizedTypeReference<?>) {
httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest,
(ParameterizedTypeReference<?>) expectedResponseType);
if (uri instanceof URI) {
if (expectedResponseType instanceof ParameterizedTypeReference<?>) {
httpResponse = this.restTemplate.exchange((URI) uri, httpMethod, httpRequest,
(ParameterizedTypeReference<?>) expectedResponseType);
}
else {
httpResponse = this.restTemplate.exchange((URI) uri, httpMethod, httpRequest,
(Class<?>) expectedResponseType);
}
}
else {
httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest,
(Class<?>) expectedResponseType);
if (expectedResponseType instanceof ParameterizedTypeReference<?>) {
httpResponse = this.restTemplate.exchange((String) uri, httpMethod, httpRequest,
(ParameterizedTypeReference<?>) expectedResponseType, uriVariables);
}
else {
httpResponse = this.restTemplate.exchange((String) uri, httpMethod, httpRequest,
(Class<?>) expectedResponseType, uriVariables);
}
}
return getReply(httpResponse);
}
catch (RestClientException e) {

View File

@@ -844,12 +844,23 @@
<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,
[DEPRECATED] 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".
Deprecated since 5.3 in favor of 'encoding-mode'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="encoding-mode" default="TEMPLATE_AND_VALUES">
<xsd:annotation>
<xsd:documentation>
Set the encoding mode during URI building.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="encodingModeEnumeration xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="http-method">
<xsd:annotation>
<xsd:documentation>
@@ -943,4 +954,13 @@
</xsd:attribute>
</xsd:attributeGroup>
<xsd:simpleType name="encodingModeEnumeration">
<xsd:restriction base="xsd:token">
<xsd:enumeration value="TEMPLATE_AND_VALUES"/>
<xsd:enumeration value="VALUES_ONLY"/>
<xsd:enumeration value="URI_COMPONENT"/>
<xsd:enumeration value="NONE"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2020 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.
@@ -19,7 +19,6 @@ package org.springframework.integration.http;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.isNull;
import java.net.URI;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@@ -27,8 +26,7 @@ import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Locale;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.DirectFieldAccessor;
@@ -47,7 +45,7 @@ import org.springframework.messaging.PollableChannel;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
@@ -64,7 +62,7 @@ import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
*
* @since 3.0
*/
@RunWith(SpringRunner.class)
@SpringJUnitConfig
@DirtiesContext
public class HttpProxyScenarioTests {
@@ -122,8 +120,8 @@ public class HttpProxyScenarioTests {
final String contentDispositionValue = "attachment; filename=\"test.txt\"";
Mockito.doAnswer(invocation -> {
URI uri = invocation.getArgument(0);
assertThat(uri).isEqualTo(new URI("http://testServer/test?foo=bar&FOO=BAR"));
String uri = invocation.getArgument(0);
assertThat(uri).isEqualTo("http://testServer/test?foo=bar&FOO=BAR");
HttpEntity<?> httpEntity = (HttpEntity<?>) invocation.getArguments()[2];
HttpHeaders httpHeaders = httpEntity.getHeaders();
assertThat(httpHeaders.getIfModifiedSince()).isEqualTo(ifModifiedSince);
@@ -134,8 +132,9 @@ public class HttpProxyScenarioTests {
responseHeaders.set("Connection", "close");
responseHeaders.set("Content-Disposition", contentDispositionValue);
return new ResponseEntity<>(responseHeaders, HttpStatus.OK);
}).when(template).exchange(Mockito.any(URI.class), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), (Class<?>) isNull());
}).when(template)
.exchange(Mockito.anyString(), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), (Class<?>) isNull(), Mockito.anyMap());
PropertyAccessor dfa = new DirectFieldAccessor(this.handler);
dfa.setPropertyValue("restTemplate", template);
@@ -175,8 +174,8 @@ public class HttpProxyScenarioTests {
RestTemplate template = Mockito.spy(new RestTemplate());
Mockito.doAnswer(invocation -> {
URI uri = invocation.getArgument(0);
assertThat(uri).isEqualTo(new URI("http://testServer/testmp"));
String uri = invocation.getArgument(0);
assertThat(uri).isEqualTo("http://testServer/testmp");
HttpEntity<?> httpEntity = (HttpEntity<?>) invocation.getArguments()[2];
HttpHeaders httpHeaders = httpEntity.getHeaders();
assertThat(httpHeaders.getFirst("Connection")).isEqualTo("Keep-Alive");
@@ -191,8 +190,9 @@ public class HttpProxyScenarioTests {
responseHeaders.set("Connection", "close");
responseHeaders.set("Content-Type", "text/plain");
return new ResponseEntity<>(responseHeaders, HttpStatus.OK);
}).when(template).exchange(Mockito.any(URI.class), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), (Class<?>) isNull());
}).when(template)
.exchange(Mockito.anyString(), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), (Class<?>) isNull(), Mockito.anyMap());
PropertyAccessor dfa = new DirectFieldAccessor(this.handlermp);
dfa.setPropertyValue("restTemplate", template);

View File

@@ -31,6 +31,7 @@
request-factory="testRequestFactory"
error-handler="testErrorHandler"
order="77"
encoding-mode="VALUES_ONLY"
auto-startup="false">
<uri-variable name="foo" expression="headers.bar"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,13 +17,12 @@
package org.springframework.integration.http.config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -46,11 +45,11 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* @author Mark Fisher
@@ -60,8 +59,7 @@ import org.springframework.web.client.RestTemplate;
* @author Biju Kunjummen
* @author Shiliang Li
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SpringJUnitConfig
@DirtiesContext
public class HttpOutboundChannelAdapterParserTests {
@@ -112,13 +110,15 @@ public class HttpOutboundChannelAdapterParserTests {
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.minimalConfig, "handler.restTemplate", RestTemplate.class);
assertThat(restTemplate).isNotSameAs(customRestTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor
.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false);
assertThat(endpointAccessor.getPropertyValue("inputChannel"))
.isEqualTo(this.applicationContext.getBean("requests"));
assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull();
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
DirectFieldAccessor templateAccessor =
new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue();
@@ -126,7 +126,7 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test1");
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
.isEqualTo(HttpMethod.POST.name());
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true);
}
@@ -134,7 +134,8 @@ public class HttpOutboundChannelAdapterParserTests {
@SuppressWarnings("unchecked")
public void fullConfig() {
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.fullConfig);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor
.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false);
assertThat(endpointAccessor.getPropertyValue("inputChannel"))
@@ -142,7 +143,8 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull();
assertThat(handlerAccessor.getPropertyValue("order")).isEqualTo(77);
assertThat(endpointAccessor.getPropertyValue("autoStartup")).isEqualTo(Boolean.FALSE);
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
DirectFieldAccessor templateAccessor =
new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertThat(TestUtils.getPropertyValue(handler, "expectedResponseTypeExpression", Expression.class).getValue())
@@ -158,7 +160,7 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test2/{foo}");
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
.isEqualTo(HttpMethod.GET.name());
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(false);
Map<String, Expression> uriVariableExpressions =
(Map<String, Expression>) handlerAccessor.getPropertyValue("uriVariableExpressions");
@@ -171,6 +173,10 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(mappedResponseHeaders.length).isEqualTo(0);
assertThat(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader1")).isTrue();
assertThat(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2")).isTrue();
assertThat(
TestUtils.getPropertyValue(handler,
"restTemplate.uriTemplateHandler.encodingMode", DefaultUriBuilderFactory.EncodingMode.class))
.isEqualTo(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
}
@Test
@@ -180,10 +186,12 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(restTemplate).isEqualTo(customRestTemplate);
}
@Test(expected = BeanDefinitionParsingException.class)
@Test
public void failWithRestTemplateAndRestAttributes() {
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-fail-context.xml", this.getClass())
.close();
assertThatExceptionOfType(BeanDefinitionParsingException.class)
.isThrownBy(() ->
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-fail-context.xml",
getClass()));
}
@Test
@@ -192,13 +200,15 @@ public class HttpOutboundChannelAdapterParserTests {
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlAndTemplate, "handler.restTemplate", RestTemplate.class);
assertThat(restTemplate).isSameAs(customRestTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor
.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false);
assertThat(endpointAccessor.getPropertyValue("inputChannel"))
.isEqualTo(this.applicationContext.getBean("requests"));
assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull();
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
DirectFieldAccessor templateAccessor =
new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue();
@@ -206,7 +216,7 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test1");
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
.isEqualTo(HttpMethod.POST.name());
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true);
//INT-3055
@@ -224,13 +234,15 @@ public class HttpOutboundChannelAdapterParserTests {
RestTemplate restTemplate =
TestUtils.getPropertyValue(this.withUrlExpression, "handler.restTemplate", RestTemplate.class);
assertThat(restTemplate).isNotSameAs(customRestTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor
.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false);
assertThat(endpointAccessor.getPropertyValue("inputChannel"))
.isEqualTo(this.applicationContext.getBean("requests"));
assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull();
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
DirectFieldAccessor templateAccessor =
new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue();
@@ -239,7 +251,7 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(expression.getExpressionString()).isEqualTo("'http://localhost/test1'");
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
.isEqualTo(HttpMethod.POST.name());
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true);
}
@@ -257,13 +269,15 @@ public class HttpOutboundChannelAdapterParserTests {
TestUtils.getPropertyValue(this.withUrlExpressionAndTemplate, "handler.restTemplate",
RestTemplate.class);
assertThat(restTemplate).isSameAs(customRestTemplate);
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor
.getPropertyValue("handler");
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertThat(handlerAccessor.getPropertyValue("expectReply")).isEqualTo(false);
assertThat(endpointAccessor.getPropertyValue("inputChannel"))
.isEqualTo(this.applicationContext.getBean("requests"));
assertThat(handlerAccessor.getPropertyValue("outputChannel")).isNull();
DirectFieldAccessor templateAccessor = new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
DirectFieldAccessor templateAccessor =
new DirectFieldAccessor(handlerAccessor.getPropertyValue("restTemplate"));
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory)
templateAccessor.getPropertyValue("requestFactory");
assertThat(requestFactory instanceof SimpleClientHttpRequestFactory).isTrue();
@@ -272,7 +286,7 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(expression.getExpressionString()).isEqualTo("'http://localhost/test1'");
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
.isEqualTo(HttpMethod.POST.name());
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true);
}
@@ -281,21 +295,23 @@ public class HttpOutboundChannelAdapterParserTests {
assertThat(this.withPoller1).isInstanceOf(PollingConsumer.class);
}
@Test(expected = BeanDefinitionParsingException.class)
@Test
public void failWithUrlAndExpression() {
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-url-fail-context.xml",
this.getClass()).close();
assertThatExceptionOfType(BeanDefinitionParsingException.class)
.isThrownBy(() ->
new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterParserTests-url-fail-context.xml",
getClass()));
}
public static class StubErrorHandler implements ResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
public boolean hasError(ClientHttpResponse response) {
return false;
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
public void handleError(ClientHttpResponse response) {
}
}

View File

@@ -9,20 +9,16 @@
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<si:chain id="chain" input-channel="httpOutboundChannelAdapterWithinChain">
<outbound-channel-adapter id="adapter" url="http://localhost/test1/%2f" encode-uri="false" rest-template="restTemplate"
<outbound-channel-adapter id="adapter" url="http://localhost/test1/%2f" rest-template="restTemplate"
trusted-spel="true" />
</si:chain>
<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>
<beans:bean id="restTemplate"
class="org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests$MockRestTemplate2"/>
<si:chain input-channel="httpOutboundGatewayWithinChain" output-channel="replyChannel">
<outbound-gateway url="http://localhost:51235/%2f/testApps?param={param}"
rest-template="restTemplate"
encode-uri="false"
trusted-spel="true"
expected-response-type-expression="T (org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandlerTests).testParameterizedTypeReference()">
<uri-variable name="param" expression="T(java.net.URLEncoder).encode('http Outbound Gateway Within Chain', 'UTF-8')"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,7 +17,7 @@
package org.springframework.integration.http.outbound;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -26,7 +26,6 @@ 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;
@@ -36,8 +35,7 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.xml.transform.Source;
import org.junit.Test;
import org.mockito.Mockito;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanFactory;
@@ -59,13 +57,19 @@ import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* @author Mark Fisher
@@ -99,14 +103,11 @@ public class HttpRequestExecutingMessageHandlerTests {
Message<?> message = MessageBuilder.withPayload(form).build();
QueueChannel replyChannel = new QueueChannel();
handler.setOutputChannel(replyChannel);
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(request.getHeaders().getContentType()).isNotNull();
@@ -134,14 +135,11 @@ public class HttpRequestExecutingMessageHandlerTests {
Message<?> message = MessageBuilder.withPayload(form).build();
QueueChannel replyChannel = new QueueChannel();
handler.setOutputChannel(replyChannel);
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -168,14 +166,11 @@ public class HttpRequestExecutingMessageHandlerTests {
Message<?> message = MessageBuilder.withPayload(form).build();
QueueChannel replyChannel = new QueueChannel();
handler.setOutputChannel(replyChannel);
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof Map<?, ?>).isTrue();
@@ -202,14 +197,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("c", new String[] { "5" });
form.put("d", "6");
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -250,14 +242,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("c", new String[] { "5" });
form.put("d", "6");
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -299,14 +288,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("a", new Object[] { null, 4, null });
form.put("b", "4");
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -347,14 +333,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("a", list);
form.put("b", "4");
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -394,14 +377,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("a", list);
form.put("b", "4");
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -437,14 +417,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("b", Collections.EMPTY_LIST);
form.put("c", Collections.singletonList("3"));
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -482,14 +459,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("b", Collections.EMPTY_LIST);
form.put("c", Collections.singletonList(new City("Mohnton")));
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -524,14 +498,11 @@ public class HttpRequestExecutingMessageHandlerTests {
form.put("b", "foo");
form.put("c", null);
Message<?> message = MessageBuilder.withPayload(form).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof MultiValueMap<?, ?>).isTrue();
@@ -560,14 +531,11 @@ public class HttpRequestExecutingMessageHandlerTests {
byte[] bytes = "Hello World".getBytes();
Message<?> message = MessageBuilder.withPayload(bytes).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof byte[]).isTrue();
@@ -586,14 +554,11 @@ public class HttpRequestExecutingMessageHandlerTests {
handler.afterPropertiesSet();
Message<?> message = MessageBuilder.withPayload(mock(Source.class)).build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThat(exception.getCause().getMessage()).isEqualTo("intentional");
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message))
.withStackTraceContaining("intentional");
HttpEntity<?> request = template.lastRequestEntity.get();
Object body = request.getBody();
assertThat(body instanceof Source).isTrue();
@@ -644,89 +609,73 @@ public class HttpRequestExecutingMessageHandlerTests {
setBeanFactory(handler);
handler.afterPropertiesSet();
Message<?> message = MessageBuilder.withPayload(mock(Source.class)).build();
try {
handler.handleMessage(message);
fail("An Exception expected");
}
catch (Exception e) {
assertThat(e.getCause().getMessage()).isEqualTo("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull();
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build()))
.withStackTraceContaining("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull();
//HEAD
handler.setHttpMethod(HttpMethod.HEAD);
message = MessageBuilder.withPayload(mock(Source.class)).build();
try {
handler.handleMessage(message);
fail("An Exception expected");
}
catch (Exception e) {
assertThat(e.getCause().getMessage()).isEqualTo("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull();
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build()))
.withStackTraceContaining("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull();
//DELETE
handler.setHttpMethod(HttpMethod.DELETE);
message = MessageBuilder.withPayload(mock(Source.class)).build();
try {
handler.handleMessage(message);
fail("An Exception expected");
}
catch (Exception e) {
assertThat(e.getCause().getMessage()).isEqualTo("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_XML);
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build()))
.withStackTraceContaining("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_XML);
//TRACE
handler.setHttpMethod(HttpMethod.TRACE);
message = MessageBuilder.withPayload(mock(Source.class)).build();
try {
handler.handleMessage(message);
fail("An Exception expected");
}
catch (Exception e) {
assertThat(e.getCause().getMessage()).isEqualTo("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull();
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(MessageBuilder.withPayload(mock(Source.class)).build()))
.withStackTraceContaining("intentional");
assertThat(template.lastRequestEntity.get().getHeaders().getContentType()).isNull();
}
@Test // INT-2275
public void testOutboundChannelAdapterWithinChain() throws URISyntaxException {
@Test
public void testOutboundChannelAdapterWithinChain() {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
"HttpOutboundWithinChainTests-context.xml", this.getClass());
MessageChannel channel = ctx.getBean("httpOutboundChannelAdapterWithinChain", MessageChannel.class);
RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class);
MockRestTemplate2 restTemplate = ctx.getBean("restTemplate", MockRestTemplate2.class);
channel.send(MessageBuilder.withPayload("test").build());
Mockito.verify(restTemplate).exchange(Mockito.eq(new URI("http://localhost/test1/%2f")),
Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.<Class<Object>>eq(null));
assertThat(restTemplate.actualUrl.get()).isEqualTo("http://localhost/test1/%2f");
HttpRequestExecutingMessageHandler handler = ctx.getBean("chain$child.adapter.handler",
HttpRequestExecutingMessageHandler.class);
assertThat(TestUtils.getPropertyValue(handler, "trustedSpel")).isEqualTo(Boolean.TRUE);
ctx.close();
}
@Test // INT-1029
public void testHttpOutboundGatewayWithinChain() throws URISyntaxException {
@Test
public void testHttpOutboundGatewayWithinChain() {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
"HttpOutboundWithinChainTests-context.xml", this.getClass());
MessageChannel channel = ctx.getBean("httpOutboundGatewayWithinChain", MessageChannel.class);
RestTemplate restTemplate = ctx.getBean("restTemplate", RestTemplate.class);
MockRestTemplate2 restTemplate = ctx.getBean("restTemplate", MockRestTemplate2.class);
channel.send(MessageBuilder.withPayload("test").build());
PollableChannel output = ctx.getBean("replyChannel", PollableChannel.class);
Message<?> receive = output.receive();
assertThat(((ResponseEntity<?>) receive.getPayload()).getStatusCode()).isEqualTo(HttpStatus.OK);
Mockito.verify(restTemplate).exchange(
Mockito.eq(new URI("http://localhost:51235/%2f/testApps?param=http+Outbound+Gateway+Within+Chain")),
Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class),
Mockito.eq(new ParameterizedTypeReference<List<String>>() {
}));
assertThat(restTemplate.actualUrl.get())
.isEqualTo("http://localhost:51235/%2f/testApps?param=http+Outbound+Gateway+Within+Chain");
ctx.close();
}
@@ -739,11 +688,10 @@ public class HttpRequestExecutingMessageHandlerTests {
handler.afterPropertiesSet();
String theURL = "https://bar/baz?foo#bar";
Message<?> message = MessageBuilder.withPayload("").setHeader("foo", theURL).build();
try {
handler.handleRequestMessage(message);
}
catch (Exception e) {
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(message));
assertThat(restTemplate.actualUrl.get()).isEqualTo(theURL);
}
@@ -757,18 +705,13 @@ public class HttpRequestExecutingMessageHandlerTests {
restTemplate
);
// This flag is set by default to true, but for sake of clarity for the reader we explicitly set it here again
handler.setEncodeUri(true);
handler.setUriVariableExpressions(Collections.singletonMap("query", parser.parseExpression("payload")));
setBeanFactory(handler);
handler.afterPropertiesSet();
Message<?> message = new GenericMessage<>("test-äöü&%");
try {
handler.handleMessage(message);
}
catch (Exception ignored) {
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(new GenericMessage<>("test-äöü&%")));
assertThat(restTemplate.actualUrl.get()).isEqualTo("https://example.com?query=test-%C3%A4%C3%B6%C3%BC%26%25");
}
@@ -776,39 +719,40 @@ public class HttpRequestExecutingMessageHandlerTests {
public void testUriEncodedDisabled() {
SpelExpressionParser parser = new SpelExpressionParser();
MockRestTemplate restTemplate = new MockRestTemplate();
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory();
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
restTemplate.setUriTemplateHandler(uriBuilderFactory);
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(
"https://example.com?query={query}",
restTemplate
);
handler.setEncodeUri(false);
handler.setUriVariableExpressions(Collections.singletonMap("query", parser.parseExpression("payload")));
setBeanFactory(handler);
handler.afterPropertiesSet();
Message<?> message = new GenericMessage<>("test-äöü");
try {
handler.handleMessage(message);
}
catch (Exception ignored) {
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(new GenericMessage<>("test-äöü")));
assertThat(restTemplate.actualUrl.get()).isEqualTo("https://example.com?query=test-äöü");
}
@Test
public void testInt2455UriNotEncoded() {
MockRestTemplate restTemplate = new MockRestTemplate();
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory();
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
restTemplate.setUriTemplateHandler(uriBuilderFactory);
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(
new SpelExpressionParser().parseExpression("'https://my.RabbitMQ.com/api/' + payload"), restTemplate);
handler.setEncodeUri(false);
setBeanFactory(handler);
handler.afterPropertiesSet();
Message<?> message = MessageBuilder.withPayload("queues/%2f/si.test.queue?foo#bar").build();
try {
handler.handleRequestMessage(message);
}
catch (Exception e) {
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(new GenericMessage<>("queues/%2f/si.test.queue?foo#bar")));
assertThat(restTemplate.actualUrl.get())
.isEqualTo("https://my.RabbitMQ.com/api/queues/%2f/si.test.queue?foo#bar");
}
@@ -830,17 +774,12 @@ public class HttpRequestExecutingMessageHandlerTests {
HttpHeaders requestHeaders = setUpMocksToCaptureSentHeaders(restTemplate);
Message<?> message = MessageBuilder.withPayload("foo").build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(new GenericMessage<>("foo")))
.withStackTraceContaining("404 Not Found");
assertThat(requestHeaders.getAccept()).isNotNull();
assertThat(requestHeaders.getAccept().size() > 0).isTrue();
assertThat(exception.getCause().getMessage()).contains("404 Not Found");
List<MediaType> accept = requestHeaders.getAccept();
assertThat(accept.size() > 0).isTrue();
assertThat(accept.get(0).getType()).isEqualTo("application");
@@ -866,17 +805,12 @@ public class HttpRequestExecutingMessageHandlerTests {
HttpHeaders requestHeaders = setUpMocksToCaptureSentHeaders(restTemplate);
Message<?> message = MessageBuilder.withPayload("foo").build();
Exception exception = null;
try {
handler.handleMessage(message);
}
catch (Exception e) {
exception = e;
}
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> handler.handleMessage(new GenericMessage<>("foo")))
.withStackTraceContaining("404 Not Found");
assertThat(requestHeaders.getAccept()).isNotNull();
assertThat(requestHeaders.getAccept().size() > 0).isTrue();
assertThat(exception.getCause().getMessage()).contains("404 Not Found");
List<MediaType> accept = requestHeaders.getAccept();
assertThat(accept.size() > 0).isTrue();
assertThat(accept.get(0).getType()).isEqualTo("application");
@@ -933,12 +867,11 @@ public class HttpRequestExecutingMessageHandlerTests {
private final AtomicReference<String> actualUrl = new AtomicReference<>();
@Override
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);
@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
this.actualUrl.set(url.toString());
this.lastRequestEntity.set(TestUtils.getPropertyValue(requestCallback, "requestEntity", HttpEntity.class));
throw new RuntimeException("intentional");
}
@@ -947,16 +880,25 @@ public class HttpRequestExecutingMessageHandlerTests {
@SuppressWarnings("unused")
private static class MockRestTemplate2 extends RestTemplate {
@Override
public <T> ResponseEntity<T> exchange(URI uri, HttpMethod method, HttpEntity<?> requestEntity,
Class<T> responseType) throws RestClientException {
return new ResponseEntity<T>(HttpStatus.OK);
private final AtomicReference<String> actualUrl = new AtomicReference<>();
MockRestTemplate2() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory();
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
setUriTemplateHandler(uriBuilderFactory);
}
@Override
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType) throws RestClientException {
return new ResponseEntity<T>(HttpStatus.OK);
@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
this.actualUrl.set(url.toString());
try {
return responseExtractor.extractData(new MockClientHttpResponse(new byte[0], HttpStatus.OK));
}
catch (IOException ex) {
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + url + "\": " + ex.getMessage(), ex);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.http.config.HttpOutboundChannelAdapterParser;
import org.springframework.integration.webflux.outbound.WebFluxRequestExecutingMessageHandler;
import org.springframework.util.StringUtils;
@@ -52,6 +53,9 @@ public class WebFluxOutboundChannelAdapterParser extends HttpOutboundChannelAdap
.getConstructorArgumentValues()
.addIndexedArgumentValue(1, new RuntimeBeanReference(webClientRef));
}
else {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode");
}
String type = element.getAttribute("publisher-element-type");
String typeExpression = element.getAttribute("publisher-element-type-expression");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 the original author or authors.
* Copyright 2017-2020 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.
@@ -18,7 +18,7 @@ package org.springframework.integration.webflux.outbound;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.function.Supplier;
import java.util.Map;
import org.reactivestreams.Publisher;
@@ -50,6 +50,7 @@ import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.util.DefaultUriBuilderFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -71,6 +72,8 @@ public class WebFluxRequestExecutingMessageHandler extends AbstractHttpRequestEx
private final WebClient webClient;
private final boolean webClientExplicitlySet;
private boolean replyPayloadToFlux;
private BodyExtractor<?, ClientHttpResponse> bodyExtractor;
@@ -124,10 +127,26 @@ public class WebFluxRequestExecutingMessageHandler extends AbstractHttpRequestEx
*/
public WebFluxRequestExecutingMessageHandler(Expression uriExpression, @Nullable WebClient webClient) {
super(uriExpression);
this.webClient = (webClient == null ? WebClient.create() : webClient);
this.webClientExplicitlySet = webClient != null;
this.webClient =
!this.webClientExplicitlySet
? WebClient.builder().uriBuilderFactory(this.uriFactory).build()
: webClient;
this.setAsync(true);
}
private void assertLocalWebClient(String option) {
Assert.isTrue(!this.webClientExplicitlySet,
() -> "The option '" + option + "' must be provided on the externally configured WebClient: "
+ this.webClient);
}
@Override
public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) {
assertLocalWebClient("encodingMode on UriBuilderFactory");
super.setEncodingMode(encodingMode);
}
/**
* The boolean flag to identify if the reply payload should be as a {@link Flux} from the response body
* or as resolved value from the {@link Mono} of the response body.
@@ -185,13 +204,20 @@ public class WebFluxRequestExecutingMessageHandler extends AbstractHttpRequestEx
}
@Override
protected Object exchange(Supplier<URI> uriSupplier, HttpMethod httpMethod, HttpEntity<?> httpRequest,
Object expectedResponseType, Message<?> requestMessage) {
protected Object exchange(Object uri, HttpMethod httpMethod, HttpEntity<?> httpRequest,
Object expectedResponseType, Message<?> requestMessage, Map<String, ?> uriVariables) {
WebClient.RequestBodySpec requestSpec =
this.webClient.method(httpMethod)
.uri(b -> uriSupplier.get())
.headers(headers -> headers.putAll(httpRequest.getHeaders()));
WebClient.RequestBodyUriSpec requestBodyUriSpec = this.webClient.method(httpMethod);
WebClient.RequestBodySpec requestSpec;
if (uri instanceof URI) {
requestSpec = requestBodyUriSpec.uri((URI) uri);
}
else {
requestSpec = requestBodyUriSpec.uri((String) uri, uriVariables);
}
requestSpec = requestSpec.headers(headers -> headers.putAll(httpRequest.getHeaders()));
BodyInserter<?, ? super ClientHttpRequest> inserter = buildBodyInserterForRequest(requestMessage, httpRequest);
if (inserter != null) {
requestSpec.body(inserter);

View File

@@ -10,7 +10,8 @@
<si:channel id="requests"/>
<outbound-channel-adapter id="reactiveMinimalConfig" url="http://localhost/test1" channel="requests"/>
<outbound-channel-adapter id="reactiveMinimalConfig" url="http://localhost/test1" channel="requests"
encoding-mode="VALUES_ONLY"/>
<outbound-channel-adapter id="reactiveWebClientConfig" url="http://localhost/test1" channel="requests"
web-client="webClient"

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 the original author or authors.
* Copyright 2017-2020 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.
@@ -18,11 +18,10 @@ package org.springframework.integration.webflux.config;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,15 +32,16 @@ import org.springframework.http.HttpMethod;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* @author Artem Bilan
*
* @since 5.0
*/
@RunWith(SpringRunner.class)
@SpringJUnitConfig
@DirtiesContext
public class WebFluxOutboundChannelAdapterParserTests {
@@ -75,8 +75,12 @@ public class WebFluxOutboundChannelAdapterParserTests {
assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test1");
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
.isEqualTo(HttpMethod.POST.name());
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true);
assertThat(
TestUtils.getPropertyValue(handler, "webClient.uriBuilderFactory.encodingMode",
DefaultUriBuilderFactory.EncodingMode.class))
.isEqualTo(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
}
@Test

View File

@@ -703,12 +703,13 @@ List<NameValuePair> nameValuePairs =
----
====
[[http-uri-encoding]]
==== Controlling URI Encoding
By default, the URL string is encoded (see https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html[`UriComponentsBuilder`]) to the URI object before sending the request.
In some scenarios with a non-standard URI (such as the RabbitMQ REST API), it is undesirable to perform the encoding.
The `<http:outbound-gateway/>` and `<http:outbound-channel-adapter/>` provide an `encode-uri` attribute.
To disable encoding the URL, set this attribute to `false` (by default, it is `true`).
The `<http:outbound-gateway/>` and `<http:outbound-channel-adapter/>` provide an `encoding-mode` attribute.
To disable encoding the URL, set this attribute to `NONE` (by default, it is `TEMPLATE_AND_VALUES`).
If you wish to partially encode some of the URL, use an `expression` within a `<uri-variable/>`, as the following example shows:
====
@@ -722,6 +723,10 @@ If you wish to partially encode some of the URL, use an `expression` within a `<
----
====
With Java DSL this option can be controlled by the `BaseHttpMessageHandlerSpec.encodingMode()` option.
Same configuration applies for similar outbound components in the <<./webflux.adoc/[webflux,WebFlux module>>.
For much sophisticated scenarios it is recommended to configure an `UriTemplateHandler` on the externally provided `RestTemplate`; or in case of WebFlux - `WebClient` with it `UriBuilderFactory`.
[[http-java-config]]
=== Configuring HTTP Endpoints with Java

View File

@@ -41,3 +41,10 @@ See <<./gateway.adoc/gateway-calling-default-methods,Invoking `default` Methods>
Internal components (such as `_org.springframework.integration.errorLogger`) now have a shortened name when they are represented in the integration graph.
See <<./graph.adoc#integration-graph,Integration Graph>> for more information.
[[x5.3-http]]
=== HTTP Changes
The `encodeUri` property on the `AbstractHttpRequestExecutingMessageHandler` has been deprecated in favor of newly introduced `encodingMode`.
See `DefaultUriBuilderFactory.EncodingMode` JavaDocs and <<./http.adoc/http-uri-encoding,Controlling URI Encoding>> for more information.
This also effects `WebFluxRequestExecutingMessageHandler`, respective Java DSL and XML configuration.