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.
@@ -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