diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java index 2decfbdd9f..66d2f0b215 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2021 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. @@ -29,8 +29,6 @@ import java.util.Map.Entry; import javax.xml.transform.Source; -import org.reactivestreams.Publisher; - import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.core.ParameterizedTypeReference; @@ -117,10 +115,9 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac } /** - * Specify whether the real URI should be encoded after uriVariables - * expanding and before send request via - * {@link org.springframework.web.client.RestTemplate}. The default value is - * true. + * Specify whether the real URI should be encoded after {@code uriVariables} + * expanding and before send request via {@link org.springframework.web.client.RestTemplate}. + * The default value is {@code true}. * @param encodeUri true if the URI should be encoded. * @see org.springframework.web.util.UriComponentsBuilder * @deprecated since 5.3 in favor of {@link #setEncodingMode} @@ -297,10 +294,9 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac @Nullable protected Object handleRequestMessage(Message requestMessage) { HttpMethod httpMethod = determineHttpMethod(requestMessage); - if (this.extractPayloadExplicitlySet && logger.isWarnEnabled() && !shouldIncludeRequestBody(httpMethod)) { - logger.warn("The 'extractPayload' attribute has no relevance for the current request " + - "since the HTTP Method is '" + httpMethod + - "', and no request body will be sent for that method."); + if (this.extractPayloadExplicitlySet && !shouldIncludeRequestBody(httpMethod)) { + logger.warn(() -> "The 'extractPayload' attribute has no relevance for the current request " + + "since the HTTP Method is '" + httpMethod + "', and no request body will be sent for that method."); } Object expectedResponseType = determineExpectedResponseType(requestMessage); @@ -338,7 +334,6 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac replyBuilder = (responseBody instanceof Message) ? messageBuilderFactory.fromMessage((Message) responseBody) : messageBuilderFactory.withPayload(responseBody); // NOSONAR - hasBody() - } else { replyBuilder = messageBuilderFactory.withPayload(httpResponse); @@ -362,9 +357,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac if (keyName != null) { Object cookies = headers.remove(keyName); headers.put(HttpHeaders.COOKIE, cookies); - if (logger.isDebugEnabled()) { - logger.debug("Converted Set-Cookie header to Cookie for: " + cookies); - } + logger.debug(() -> "Converted Set-Cookie header to Cookie for: " + cookies); } } @@ -389,7 +382,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac if (httpHeaders.getContentType() == null) { MediaType contentType = payload instanceof String - ? new MediaType("text", "plain", this.charset) + ? new MediaType(MediaType.TEXT_PLAIN, this.charset) : resolveContentType(payload); httpHeaders.setContentType(contentType); } @@ -408,7 +401,6 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac private HttpEntity createHttpEntityFromMessage(Message message, HttpMethod httpMethod) { HttpHeaders httpHeaders = mapHeaders(message); if (shouldIncludeRequestBody(httpMethod)) { - httpHeaders.setContentType(new MediaType("application", "x-java-serialized-object")); return new HttpEntity(message, httpHeaders); } return new HttpEntity<>(httpHeaders); @@ -421,6 +413,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac } @SuppressWarnings("unchecked") + @Nullable private MediaType resolveContentType(Object content) { MediaType contentType = null; if (content instanceof byte[]) { @@ -439,9 +432,6 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac contentType = MediaType.APPLICATION_FORM_URLENCODED; } } - if (contentType == null && !(content instanceof Publisher)) { - contentType = new MediaType("application", "x-java-serialized-object"); - } return contentType; } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java index aa351624a1..1275fe1467 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.Serializable; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -71,6 +72,9 @@ import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; +import reactor.core.publisher.Sinks; +import reactor.test.StepVerifier; + /** * @author Mark Fisher * @author Oleg Zhurakousky @@ -178,8 +182,7 @@ public class HttpRequestExecutingMessageHandlerTests { assertThat(map.get(1).toString()).isEqualTo("Philadelphia"); assertThat(map.get(2).toString()).isEqualTo("Ambler"); assertThat(map.get(3).toString()).isEqualTo("Mohnton"); - assertThat(request.getHeaders().getContentType().getType()).isEqualTo("application"); - assertThat(request.getHeaders().getContentType().getSubtype()).isEqualTo("x-java-serialized-object"); + assertThat(request.getHeaders().getContentType()).isNull(); } @Test @@ -817,6 +820,43 @@ public class HttpRequestExecutingMessageHandlerTests { assertThat(accept.get(0).getSubtype()).isEqualTo("x-java-serialized-object"); } + @Test + public void testNoContentTypeAndSmartConverter() throws IOException { + Sinks.One httpHeadersSink = Sinks.one(); + RestTemplate testRestTemplate = new RestTemplate() { + @Nullable + protected T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, + @Nullable ResponseExtractor responseExtractor) throws RestClientException { + + try { + ClientHttpRequest request = createRequest(url, method); + requestCallback.doWithRequest(request); + httpHeadersSink.tryEmitValue(request.getHeaders()); + } + catch (IOException e) { + throw new RestClientException("Not possible", e); + } + throw new RuntimeException("intentional"); + } + }; + + HttpRequestExecutingMessageHandler handler = + new HttpRequestExecutingMessageHandler("https://www.springsource.org/spring-integration", + testRestTemplate); + setBeanFactory(handler); + handler.afterPropertiesSet(); + + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> handler.handleMessage(new GenericMessage<>(new City("London")))); + + StepVerifier.create(httpHeadersSink.asMono()) + .assertNext(headers -> + assertThat(headers) + .containsEntry(HttpHeaders.CONTENT_TYPE, + Arrays.asList(MediaType.APPLICATION_JSON_VALUE))) + .verifyComplete(); + } + private void setBeanFactory(HttpRequestExecutingMessageHandler handler) { handler.setBeanFactory(mock(BeanFactory.class)); } @@ -854,6 +894,10 @@ public class HttpRequestExecutingMessageHandlerTests { this.name = name; } + public String getName() { + return this.name; + } + @Override public String toString() { return name; diff --git a/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java b/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java index e97bff1c98..f7e26acaa3 100644 --- a/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java +++ b/spring-integration-webflux/src/test/java/org/springframework/integration/webflux/dsl/WebFluxDslTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2021 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. @@ -27,7 +27,6 @@ import java.util.Collections; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.reactivestreams.Publisher; @@ -105,7 +104,6 @@ import reactor.test.StepVerifier; */ @SpringJUnitWebConfig @DirtiesContext -@Disabled public class WebFluxDslTests { @Autowired @@ -480,7 +478,7 @@ public class WebFluxDslTests { .from(WebFlux.inboundGateway("/sse") .requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)) .mappedResponseHeaders("*")) - .enrichHeaders(Collections.singletonMap("aHeader", new String[]{"foo", "bar", "baz"})) + .enrichHeaders(Collections.singletonMap("aHeader", new String[]{ "foo", "bar", "baz" })) .handle((p, h) -> Flux.fromArray(h.get("aHeader", String[].class))) .get(); } diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 96be611fde..55ce4f9328 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -36,3 +36,10 @@ See <<./amqp.adoc#amqp,AMQP Support>> for more information. The `ReactiveRedisStreamMessageProducer` has now setters for all the `StreamReceiver.StreamReceiverOptionsBuilder` options, including an `onErrorResume` function. See <<./redis.adoc#redis,Redis Support>> for more information. + +[[x5.5-http]] +==== HTTP Changes + +The `HttpRequestExecutingMessageHandler` doesn't fallback to the `application/x-java-serialized-object` content type any more and lets the `RestTemplate` make the final decision for the request body conversion based on the `HttpMessageConverter` provided. + +See <<./http.adoc#http,HTTP Support>> for more information.