Skip to content

Commit 27e1f3a

Browse files
authored
spring-projectsGH-3529: Add HTTP & WebFlux extractResponseBody (spring-projects#3530)
* spring-projectsGH-3529: Add HTTP & WebFlux extractResponseBody Fixes spring-projects#3529 * Expose a convenient `extractResponseBody` option on the HTTP client components to let end-user to decide if the body of `ResponseEntity` must be extracted (default) or the whole `ResponseEntity` should be produced as a reply message payload * Remove a deprecated since `5.3` `encode-uri` option * Document the new feature * Rework `webflux.adoc` chapter for the code snippet switcher * * Fix language in Docs * Mention a default value in JavaDocs
1 parent 0549dbd commit 27e1f3a

File tree

15 files changed

+283
-299
lines changed

15 files changed

+283
-299
lines changed

spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParser.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -90,7 +90,6 @@ protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parser
9090
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
9191
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
9292
}
93-
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
9493
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode");
9594
}
9695
return builder;

spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpOutboundGatewayParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -83,6 +83,7 @@ protected BeanDefinitionBuilder parseHandler(Element element, ParserContext pars
8383
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
8484
HttpAdapterParsingUtils.configureUriVariableExpressions(builder, parserContext, element);
8585
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "transfer-cookies");
86+
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-response-body");
8687
return builder;
8788
}
8889

@@ -102,7 +103,6 @@ protected BeanDefinitionBuilder getBuilder(Element element, ParserContext parser
102103
for (String referenceAttributeName : HttpAdapterParsingUtils.SYNC_REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
103104
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, referenceAttributeName);
104105
}
105-
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
106106
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding-mode");
107107
}
108108
return builder;

spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2020 the original author or authors.
2+
* Copyright 2017-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import org.springframework.expression.Expression;
2626
import org.springframework.http.HttpHeaders;
2727
import org.springframework.http.HttpMethod;
28+
import org.springframework.http.ResponseEntity;
2829
import org.springframework.integration.dsl.ComponentsRegistration;
2930
import org.springframework.integration.dsl.MessageHandlerSpec;
3031
import org.springframework.integration.expression.FunctionExpression;
@@ -67,19 +68,6 @@ protected S expectReply(boolean expectReply) {
6768
return _this();
6869
}
6970

70-
/**
71-
* Specify whether the real URI should be encoded after <code>uriVariables</code>
72-
* expanding and before send request via underlying implementation. The default value is <code>true</code>.
73-
* @param encodeUri true if the URI should be encoded.
74-
* @return the spec
75-
* @deprecated since 5.3 in favor of {@link #encodingMode}
76-
*/
77-
@Deprecated
78-
public S encodeUri(boolean encodeUri) {
79-
this.target.setEncodeUri(encodeUri);
80-
return _this();
81-
}
82-
8371
/**
8472
* Specify a {@link DefaultUriBuilderFactory.EncodingMode} for uri construction.
8573
* @param encodingMode to use for uri construction.
@@ -314,6 +302,7 @@ public <P> S uriVariablesFunction(Function<Message<P>, Map<String, ?>> uriVariab
314302
/**
315303
* Set to {@code true} if you wish {@code Set-Cookie} header in response to be
316304
* transferred as {@code Cookie} header in subsequent interaction for a message.
305+
* Defaults to false.
317306
* @param transferCookies the transferCookies to set.
318307
* @return the current Spec.
319308
*/
@@ -322,6 +311,18 @@ public S transferCookies(boolean transferCookies) {
322311
return _this();
323312
}
324313

314+
/**
315+
* The flag to extract a body of the {@link ResponseEntity} for reply message payload.
316+
* Defaults to true.
317+
* @param extractResponseBody produce a reply message with a whole {@link ResponseEntity} or just its body.
318+
* @return the current Spec.
319+
* @since 5.5
320+
*/
321+
public S extractResponseBody(boolean extractResponseBody) {
322+
this.target.setExtractResponseBody(extractResponseBody);
323+
return _this();
324+
}
325+
325326
@Override
326327
public Map<Object, String> getComponentsToRegister() {
327328
this.target.setUriVariableExpressions(this.uriVariableExpressions);

spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac
101101

102102
private boolean extractPayloadExplicitlySet = false;
103103

104+
private boolean extractResponseBody = true;
105+
104106
private Charset charset = StandardCharsets.UTF_8;
105107

106108
private boolean transferCookies = false;
@@ -114,22 +116,6 @@ public AbstractHttpRequestExecutingMessageHandler(Expression uriExpression) {
114116
this.uriExpression = uriExpression;
115117
}
116118

117-
/**
118-
* Specify whether the real URI should be encoded after {@code uriVariables}
119-
* expanding and before send request via {@link org.springframework.web.client.RestTemplate}.
120-
* The default value is {@code true}.
121-
* @param encodeUri true if the URI should be encoded.
122-
* @see org.springframework.web.util.UriComponentsBuilder
123-
* @deprecated since 5.3 in favor of {@link #setEncodingMode}
124-
*/
125-
@Deprecated
126-
public void setEncodeUri(boolean encodeUri) {
127-
setEncodingMode(
128-
encodeUri
129-
? DefaultUriBuilderFactory.EncodingMode.TEMPLATE_AND_VALUES
130-
: DefaultUriBuilderFactory.EncodingMode.NONE);
131-
}
132-
133119
/**
134120
* Set the encoding mode to use.
135121
* By default this is set to {@link DefaultUriBuilderFactory.EncodingMode#TEMPLATE_AND_VALUES}.
@@ -202,9 +188,8 @@ public void setExpectReply(boolean expectReply) {
202188
}
203189

204190
/**
205-
* Specify the expected response type for the REST request
206-
* otherwise the default response type is {@link ResponseEntity} and will
207-
* be returned as a payload of the reply Message.
191+
* Specify the expected response type for the REST request.
192+
* Otherwise it is null and an empty {@link ResponseEntity} is returned from HTTP client.
208193
* To take advantage of the HttpMessageConverters
209194
* registered on this adapter, provide a different type).
210195
* @param expectedResponseType The expected type.
@@ -259,8 +244,8 @@ public void setUriVariablesExpression(Expression uriVariablesExpression) {
259244

260245
/**
261246
* Set to true if you wish 'Set-Cookie' headers in responses to be
262-
* transferred as 'Cookie' headers in subsequent interactions for
263-
* a message.
247+
* transferred as 'Cookie' headers in subsequent interactions for a message.
248+
* Defaults to false.
264249
* @param transferCookies the transferCookies to set.
265250
*/
266251
public void setTransferCookies(boolean transferCookies) {
@@ -278,6 +263,16 @@ public void setTrustedSpel(boolean trustedSpel) {
278263
this.trustedSpel = trustedSpel;
279264
}
280265

266+
/**
267+
* The flag to extract a body of the {@link ResponseEntity} for reply message payload.
268+
* Defaults to true.
269+
* @param extractResponseBody produce a reply message with a whole {@link ResponseEntity} or just its body.
270+
* @since 5.5
271+
*/
272+
public void setExtractResponseBody(boolean extractResponseBody) {
273+
this.extractResponseBody = extractResponseBody;
274+
}
275+
281276
@Override
282277
public IntegrationPatternType getIntegrationPatternType() {
283278
return this.expectReply ? super.getIntegrationPatternType() : IntegrationPatternType.outbound_channel_adapter;
@@ -329,7 +324,7 @@ protected Object getReply(ResponseEntity<?> httpResponse) {
329324

330325
AbstractIntegrationMessageBuilder<?> replyBuilder;
331326
MessageBuilderFactory messageBuilderFactory = getMessageBuilderFactory();
332-
if (httpResponse.hasBody()) {
327+
if (httpResponse.hasBody() && this.extractResponseBody) {
333328
Object responseBody = httpResponse.getBody();
334329
replyBuilder = (responseBody instanceof Message<?>)
335330
? messageBuilderFactory.fromMessage((Message<?>) responseBody)

spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http.xsd

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,17 @@
536536
<xsd:union memberTypes="xsd:boolean xsd:string"/>
537537
</xsd:simpleType>
538538
</xsd:attribute>
539+
<xsd:attribute name="extract-response-body" default="true">
540+
<xsd:annotation>
541+
<xsd:documentation>
542+
Set to 'false' to return the whole 'ResponseEntity' in the reply message payload.
543+
The default value is 'true'.
544+
</xsd:documentation>
545+
</xsd:annotation>
546+
<xsd:simpleType>
547+
<xsd:union memberTypes="xsd:boolean xsd:string"/>
548+
</xsd:simpleType>
549+
</xsd:attribute>
539550
</xsd:extension>
540551
</xsd:complexContent>
541552
</xsd:complexType>
@@ -841,16 +852,6 @@
841852
]]></xsd:documentation>
842853
</xsd:annotation>
843854
</xsd:attribute>
844-
<xsd:attribute name="encode-uri" type="xsd:string" default="true">
845-
<xsd:annotation>
846-
<xsd:documentation>
847-
[DEPRECATED] When set to "false", the real URI won't be encoded before the request is sent.
848-
This may be useful in some scenarios as it allows user control over the encoding, if needed,
849-
for example by using the "url-expression". Default is "true".
850-
Deprecated since 5.3 in favor of 'encoding-mode'.
851-
</xsd:documentation>
852-
</xsd:annotation>
853-
</xsd:attribute>
854855
<xsd:attribute name="encoding-mode" default="TEMPLATE_AND_VALUES">
855856
<xsd:annotation>
856857
<xsd:documentation>
@@ -892,7 +893,7 @@
892893
<xsd:annotation>
893894
<xsd:documentation>
894895
The expected type to which the response body should be converted.
895-
Default is 'org.springframework.http.ResponseEntity'.
896+
Default is 'null' - no body expected.
896897
This attribute cannot be provided if expected-response-type-expression has a value
897898
</xsd:documentation>
898899
<xsd:appinfo>

spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
charset="UTF-8"
3737
order="77"
3838
auto-startup="false"
39-
transfer-cookies="true">
39+
transfer-cookies="true"
40+
extract-response-body="false">
4041
<uri-variable name="foo" expression="headers.bar"/>
4142
</outbound-gateway>
4243

spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,16 +17,13 @@
1717
package org.springframework.integration.http.config;
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
20-
import static org.assertj.core.api.Assertions.fail;
20+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2121

22-
import java.io.IOException;
23-
import java.nio.charset.Charset;
22+
import java.nio.charset.StandardCharsets;
2423
import java.util.Map;
2524

26-
import org.junit.Test;
27-
import org.junit.runner.RunWith;
25+
import org.junit.jupiter.api.Test;
2826

29-
import org.springframework.beans.BeansException;
3027
import org.springframework.beans.DirectFieldAccessor;
3128
import org.springframework.beans.factory.annotation.Autowired;
3229
import org.springframework.beans.factory.annotation.Qualifier;
@@ -48,8 +45,7 @@
4845
import org.springframework.messaging.MessageChannel;
4946
import org.springframework.messaging.support.GenericMessage;
5047
import org.springframework.test.annotation.DirtiesContext;
51-
import org.springframework.test.context.ContextConfiguration;
52-
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
48+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
5349
import org.springframework.util.ObjectUtils;
5450
import org.springframework.web.client.ResponseErrorHandler;
5551

@@ -59,8 +55,7 @@
5955
* @author Artem Bilan
6056
* @author Biju Kunjummen
6157
*/
62-
@RunWith(SpringJUnit4ClassRunner.class)
63-
@ContextConfiguration
58+
@SpringJUnitConfig
6459
@DirtiesContext
6560
public class HttpOutboundGatewayParserTests {
6661

@@ -107,16 +102,17 @@ public void minimalConfig() {
107102
assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test1");
108103
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
109104
.isEqualTo(HttpMethod.POST.name());
110-
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
105+
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
111106
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true);
112107
assertThat(handlerAccessor.getPropertyValue("transferCookies")).isEqualTo(false);
113108
}
114109

115110
@Test
116111
@SuppressWarnings("unchecked")
117-
public void fullConfig() throws Exception {
112+
public void fullConfig() {
118113
DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.fullConfigEndpoint);
119-
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
114+
HttpRequestExecutingMessageHandler handler =
115+
(HttpRequestExecutingMessageHandler) endpointAccessor.getPropertyValue("handler");
120116
MessageChannel requestChannel = (MessageChannel) new DirectFieldAccessor(
121117
this.fullConfigEndpoint).getPropertyValue("inputChannel");
122118
assertThat(requestChannel).isEqualTo(this.applicationContext.getBean("requests"));
@@ -139,7 +135,7 @@ public void fullConfig() throws Exception {
139135
assertThat(uriExpression.getValue()).isEqualTo("http://localhost/test2");
140136
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
141137
.isEqualTo(HttpMethod.PUT.name());
142-
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
138+
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
143139
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(false);
144140
Object requestFactoryBean = this.applicationContext.getBean("testRequestFactory");
145141
assertThat(requestFactory).isEqualTo(requestFactoryBean);
@@ -161,6 +157,7 @@ public void fullConfig() throws Exception {
161157
assertThat(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2")).isTrue();
162158
assertThat(mappedResponseHeaders[0]).isEqualTo("responseHeader");
163159
assertThat(handlerAccessor.getPropertyValue("transferCookies")).isEqualTo(true);
160+
assertThat(handlerAccessor.getPropertyValue("extractResponseBody")).isEqualTo(false);
164161
}
165162

166163
@Test
@@ -182,7 +179,7 @@ public void withUrlExpression() {
182179
assertThat(expression.getExpressionString()).isEqualTo("'http://localhost/test1'");
183180
assertThat(TestUtils.getPropertyValue(handler, "httpMethodExpression", Expression.class).getExpressionString())
184181
.isEqualTo(HttpMethod.POST.name());
185-
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(Charset.forName("UTF-8"));
182+
assertThat(handlerAccessor.getPropertyValue("charset")).isEqualTo(StandardCharsets.UTF_8);
186183
assertThat(handlerAccessor.getPropertyValue("extractPayload")).isEqualTo(true);
187184
assertThat(handlerAccessor.getPropertyValue("transferCookies")).isEqualTo(false);
188185

@@ -199,21 +196,17 @@ public void withUrlExpression() {
199196
public void withAdvice() {
200197
HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) new DirectFieldAccessor(
201198
this.withAdvice).getPropertyValue("handler");
202-
handler.handleMessage(new GenericMessage<String>("foo"));
199+
handler.handleMessage(new GenericMessage<>("foo"));
203200
assertThat(adviceCalled).isEqualTo(1);
204201
}
205202

206203
@Test
207204
public void testInt2718FailForGatewayRequestChannelAttribute() {
208-
try {
209-
new ClassPathXmlApplicationContext("HttpOutboundGatewayWithinChainTests-fail-context.xml", this.getClass())
210-
.close();
211-
fail("Expected BeanDefinitionParsingException");
212-
}
213-
catch (BeansException e) {
214-
assertThat(e instanceof BeanDefinitionParsingException).isTrue();
215-
assertThat(e.getMessage().contains("'request-channel' attribute isn't allowed for a nested")).isTrue();
216-
}
205+
assertThatExceptionOfType(BeanDefinitionParsingException.class)
206+
.isThrownBy(() ->
207+
new ClassPathXmlApplicationContext("HttpOutboundGatewayWithinChainTests-fail-context.xml",
208+
getClass()))
209+
.withMessageContaining("'request-channel' attribute isn't allowed for a nested");
217210
}
218211

219212
@Test
@@ -225,13 +218,14 @@ public void withPoller() {
225218
public static class StubErrorHandler implements ResponseErrorHandler {
226219

227220
@Override
228-
public boolean hasError(ClientHttpResponse response) throws IOException {
221+
public boolean hasError(ClientHttpResponse response) {
229222
return false;
230223
}
231224

232225
@Override
233-
public void handleError(ClientHttpResponse response) throws IOException {
226+
public void handleError(ClientHttpResponse response) {
234227
}
228+
235229
}
236230

237231
public static class FooAdvice extends AbstractRequestHandlerAdvice {

spring-integration-http/src/test/java/org/springframework/integration/http/dsl/HttpDslTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -363,6 +363,7 @@ public IntegrationFlow httpProxyFlow() {
363363
.handle(Http.outboundGateway("/service/internal?{params}")
364364
.uriVariable("params", "payload")
365365
.expectedResponseType(String.class)
366+
.extractResponseBody(false)
366367
.errorHandler(new HttpProxyResponseErrorHandler()),
367368
e -> e.id("serviceInternalGateway"))
368369
.get();

0 commit comments

Comments
 (0)