GH-2806: Add generics support to HTTP Inbound
Fixes https://github.com/spring-projects/spring-integration/issues/2806 **Cherry-pick to 5.1.x**
This commit is contained in:
committed by
Gary Russell
parent
8d9903981e
commit
df697c53e2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2017-2018 the original author or authors.
|
* Copyright 2017-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -66,23 +66,23 @@ public class BaseHttpInboundEndpoint extends MessagingGatewaySupport implements
|
|||||||
|
|
||||||
protected final AtomicInteger activeCount = new AtomicInteger();
|
protected final AtomicInteger activeCount = new AtomicInteger();
|
||||||
|
|
||||||
private volatile ResolvableType requestPayloadType = null;
|
private ResolvableType requestPayloadType = null;
|
||||||
|
|
||||||
private volatile HeaderMapper<HttpHeaders> headerMapper = DefaultHttpHeaderMapper.inboundMapper();
|
private HeaderMapper<HttpHeaders> headerMapper = DefaultHttpHeaderMapper.inboundMapper();
|
||||||
|
|
||||||
private volatile boolean extractReplyPayload = true;
|
private boolean extractReplyPayload = true;
|
||||||
|
|
||||||
private volatile Expression statusCodeExpression;
|
private Expression statusCodeExpression;
|
||||||
|
|
||||||
private volatile EvaluationContext evaluationContext;
|
private EvaluationContext evaluationContext;
|
||||||
|
|
||||||
private volatile RequestMapping requestMapping = new RequestMapping();
|
private RequestMapping requestMapping = new RequestMapping();
|
||||||
|
|
||||||
private volatile Expression payloadExpression;
|
private Expression payloadExpression;
|
||||||
|
|
||||||
private volatile Map<String, Expression> headerExpressions;
|
private Map<String, Expression> headerExpressions;
|
||||||
|
|
||||||
private volatile CrossOrigin crossOrigin;
|
private CrossOrigin crossOrigin;
|
||||||
|
|
||||||
public BaseHttpInboundEndpoint(boolean expectReply) {
|
public BaseHttpInboundEndpoint(boolean expectReply) {
|
||||||
super(expectReply);
|
super(expectReply);
|
||||||
@@ -279,8 +279,13 @@ public class BaseHttpInboundEndpoint extends MessagingGatewaySupport implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected HttpStatus evaluateHttpStatus(HttpEntity<?> httpEntity) {
|
protected HttpStatus evaluateHttpStatus(HttpEntity<?> httpEntity) {
|
||||||
Object value = this.statusCodeExpression.getValue(this.evaluationContext, httpEntity);
|
if (this.statusCodeExpression != null) {
|
||||||
return buildHttpStatus(value);
|
Object value = this.statusCodeExpression.getValue(this.evaluationContext, httpEntity);
|
||||||
|
return buildHttpStatus(value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpStatus resolveHttpStatusFromHeaders(MessageHeaders headers) {
|
protected HttpStatus resolveHttpStatusFromHeaders(MessageHeaders headers) {
|
||||||
@@ -328,8 +333,7 @@ public class BaseHttpInboundEndpoint extends MessagingGatewaySupport implements
|
|||||||
* @return true or false if HTTP request can contain the body
|
* @return true or false if HTTP request can contain the body
|
||||||
*/
|
*/
|
||||||
protected boolean isReadable(HttpRequest request) {
|
protected boolean isReadable(HttpRequest request) {
|
||||||
HttpMethod method = request.getMethod();
|
return !(CollectionUtils.containsInstance(nonReadableBodyHttpMethods, request.getMethod()));
|
||||||
return method == null ? false : !(CollectionUtils.containsInstance(nonReadableBodyHttpMethods, method));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.integration.http.inbound;
|
package org.springframework.integration.http.inbound;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -39,6 +40,7 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||||
|
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||||
@@ -93,7 +95,7 @@ import org.springframework.web.servlet.HandlerMapping;
|
|||||||
* In a request-reply scenario, the reply Message's payload will be extracted prior
|
* In a request-reply scenario, the reply Message's payload will be extracted prior
|
||||||
* to generating a response by default.
|
* to generating a response by default.
|
||||||
* To have the entire serialized Message available for the response, switch the
|
* To have the entire serialized Message available for the response, switch the
|
||||||
* {@link #extractReplyPayload} value to {@code false}.
|
* {@code extractReplyPayload} value to {@code false}.
|
||||||
*
|
*
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
@@ -146,27 +148,21 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
stringHttpMessageConverter.setWriteAcceptCharset(false);
|
stringHttpMessageConverter.setWriteAcceptCharset(false);
|
||||||
this.defaultMessageConverters.add(stringHttpMessageConverter);
|
this.defaultMessageConverters.add(stringHttpMessageConverter);
|
||||||
this.defaultMessageConverters.add(new ResourceHttpMessageConverter());
|
this.defaultMessageConverters.add(new ResourceHttpMessageConverter());
|
||||||
SourceHttpMessageConverter<Source> sourceConverter = new SourceHttpMessageConverter<Source>();
|
SourceHttpMessageConverter<Source> sourceConverter = new SourceHttpMessageConverter<>();
|
||||||
this.defaultMessageConverters.add(sourceConverter);
|
this.defaultMessageConverters.add(sourceConverter);
|
||||||
if (jaxb2Present) {
|
if (jaxb2Present) {
|
||||||
this.defaultMessageConverters.add(new Jaxb2RootElementHttpMessageConverter());
|
this.defaultMessageConverters.add(new Jaxb2RootElementHttpMessageConverter());
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("'Jaxb2RootElementHttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
||||||
logger.debug("'Jaxb2RootElementHttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (JacksonPresent.isJackson2Present()) {
|
if (JacksonPresent.isJackson2Present()) {
|
||||||
this.defaultMessageConverters.add(new MappingJackson2HttpMessageConverter());
|
this.defaultMessageConverters.add(new MappingJackson2HttpMessageConverter());
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("'MappingJackson2HttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
||||||
logger.debug("'MappingJackson2HttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (romeToolsPresent) {
|
if (romeToolsPresent) {
|
||||||
this.defaultMessageConverters.add(new AtomFeedHttpMessageConverter());
|
this.defaultMessageConverters.add(new AtomFeedHttpMessageConverter());
|
||||||
this.defaultMessageConverters.add(new RssChannelHttpMessageConverter());
|
this.defaultMessageConverters.add(new RssChannelHttpMessageConverter());
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("'AtomFeedHttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
||||||
logger.debug("'AtomFeedHttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
logger.debug("'RssChannelHttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
||||||
logger.debug("'RssChannelHttpMessageConverter' was added to the 'defaultMessageConverters'.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,8 +172,9 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
* @param messageConverters The message converters.
|
* @param messageConverters The message converters.
|
||||||
*/
|
*/
|
||||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||||
|
Assert.notNull(messageConverters, "'messageConverters' must not be null");
|
||||||
Assert.noNullElements(messageConverters.toArray(), "'messageConverters' must not contain null entries");
|
Assert.noNullElements(messageConverters.toArray(), "'messageConverters' must not contain null entries");
|
||||||
List<HttpMessageConverter<?>> localConverters = new ArrayList<HttpMessageConverter<?>>(messageConverters);
|
List<HttpMessageConverter<?>> localConverters = new ArrayList<>(messageConverters);
|
||||||
if (this.mergeWithDefaultConverters) {
|
if (this.mergeWithDefaultConverters) {
|
||||||
localConverters.addAll(this.defaultMessageConverters);
|
localConverters.addAll(this.defaultMessageConverters);
|
||||||
this.convertersMerged = true;
|
this.convertersMerged = true;
|
||||||
@@ -266,14 +263,13 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
|
|
||||||
this.activeCount.incrementAndGet();
|
this.activeCount.incrementAndGet();
|
||||||
try {
|
try {
|
||||||
StandardEvaluationContext evaluationContext = this.createEvaluationContext();
|
StandardEvaluationContext evaluationContext = createEvaluationContext();
|
||||||
evaluationContext.setRootObject(httpEntity);
|
evaluationContext.setRootObject(httpEntity);
|
||||||
|
|
||||||
evaluationContext.setVariable("requestAttributes", RequestContextHolder.currentRequestAttributes());
|
evaluationContext.setVariable("requestAttributes", RequestContextHolder.currentRequestAttributes());
|
||||||
|
|
||||||
MultiValueMap<String, String> requestParams = this.convertParameterMap(servletRequest.getParameterMap());
|
MultiValueMap<String, String> requestParams = convertParameterMap(servletRequest.getParameterMap());
|
||||||
evaluationContext.setVariable("requestParams", requestParams);
|
evaluationContext.setVariable("requestParams", requestParams);
|
||||||
|
|
||||||
evaluationContext.setVariable("requestHeaders", new ServletServerHttpRequest(servletRequest).getHeaders());
|
evaluationContext.setVariable("requestHeaders", new ServletServerHttpRequest(servletRequest).getHeaders());
|
||||||
|
|
||||||
Cookie[] requestCookies = servletRequest.getCookies();
|
Cookie[] requestCookies = servletRequest.getCookies();
|
||||||
@@ -335,12 +331,16 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
AbstractIntegrationMessageBuilder<?> messageBuilder = null;
|
AbstractIntegrationMessageBuilder<?> messageBuilder = null;
|
||||||
|
|
||||||
if (payload instanceof Message<?>) {
|
if (payload instanceof Message<?>) {
|
||||||
messageBuilder = this.getMessageBuilderFactory().fromMessage((Message<?>) payload)
|
messageBuilder =
|
||||||
.copyHeadersIfAbsent(headers);
|
getMessageBuilderFactory()
|
||||||
|
.fromMessage((Message<?>) payload)
|
||||||
|
.copyHeadersIfAbsent(headers);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Assert.state(payload != null, "payload cannot be null");
|
Assert.state(payload != null, "payload cannot be null");
|
||||||
messageBuilder = this.getMessageBuilderFactory().withPayload(payload).copyHeaders(headers);
|
messageBuilder = getMessageBuilderFactory()
|
||||||
|
.withPayload(payload)
|
||||||
|
.copyHeaders(headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpMethod method = httpEntity.getMethod();
|
HttpMethod method = httpEntity.getMethod();
|
||||||
@@ -359,30 +359,24 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
Message<?> reply = null;
|
Message<?> reply = null;
|
||||||
if (this.expectReply) {
|
if (this.expectReply) {
|
||||||
try {
|
try {
|
||||||
reply = this.sendAndReceiveMessage(message);
|
reply = sendAndReceiveMessage(message);
|
||||||
}
|
}
|
||||||
catch (MessageTimeoutException e) {
|
catch (MessageTimeoutException e) {
|
||||||
if (getStatusCodeExpression() != null) {
|
reply =
|
||||||
reply = getMessageBuilderFactory().withPayload(e.getMessage())
|
getMessageBuilderFactory()
|
||||||
.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE,
|
.withPayload(e.getMessage())
|
||||||
evaluateHttpStatus(httpEntity))
|
.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE,
|
||||||
.build();
|
evaluateHttpStatus(httpEntity))
|
||||||
}
|
.build();
|
||||||
else {
|
|
||||||
reply = getMessageBuilderFactory().withPayload(e.getMessage())
|
|
||||||
.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE,
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.send(message);
|
send(message);
|
||||||
}
|
}
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
this.postProcessRequest(servletRequest);
|
postProcessRequest(servletRequest);
|
||||||
this.activeCount.decrementAndGet();
|
this.activeCount.decrementAndGet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,7 +385,8 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Endpoint is stopped; returning status " + HttpStatus.SERVICE_UNAVAILABLE);
|
logger.debug("Endpoint is stopped; returning status " + HttpStatus.SERVICE_UNAVAILABLE);
|
||||||
}
|
}
|
||||||
return this.getMessageBuilderFactory().withPayload("Endpoint is stopped")
|
return getMessageBuilderFactory()
|
||||||
|
.withPayload("Endpoint is stopped")
|
||||||
.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE, HttpStatus.SERVICE_UNAVAILABLE)
|
.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE, HttpStatus.SERVICE_UNAVAILABLE)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -401,7 +396,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
* sets up the {@link ServletServerHttpResponse}.
|
* sets up the {@link ServletServerHttpResponse}.
|
||||||
* @param response The ServletServerHttpResponse.
|
* @param response The ServletServerHttpResponse.
|
||||||
* @param replyMessage The reply message.
|
* @param replyMessage The reply message.
|
||||||
* @return The message payload (if {@link #extractReplyPayload}) otherwise the message.
|
* @return The message payload (if {@code extractReplyPayload}) otherwise the message.
|
||||||
*/
|
*/
|
||||||
protected final Object setupResponseAndConvertReply(ServletServerHttpResponse response, Message<?> replyMessage) {
|
protected final Object setupResponseAndConvertReply(ServletServerHttpResponse response, Message<?> replyMessage) {
|
||||||
getHeaderMapper().fromHeaders(replyMessage.getHeaders(), response.getHeaders());
|
getHeaderMapper().fromHeaders(replyMessage.getHeaders(), response.getHeaders());
|
||||||
@@ -461,7 +456,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
* Converts a servlet request's parameterMap to a {@link MultiValueMap}.
|
* Converts a servlet request's parameterMap to a {@link MultiValueMap}.
|
||||||
*/
|
*/
|
||||||
private MultiValueMap<String, String> convertParameterMap(Map<String, String[]> parameterMap) {
|
private MultiValueMap<String, String> convertParameterMap(Map<String, String[]> parameterMap) {
|
||||||
MultiValueMap<String, String> convertedMap = new LinkedMultiValueMap<String, String>(parameterMap.size());
|
MultiValueMap<String, String> convertedMap = new LinkedMultiValueMap<>(parameterMap.size());
|
||||||
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
|
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
|
||||||
String[] values = entry.getValue();
|
String[] values = entry.getValue();
|
||||||
for (String value : values) {
|
for (String value : values) {
|
||||||
@@ -487,29 +482,37 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
|
|||||||
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||||
}
|
}
|
||||||
ResolvableType requestPayloadType = getRequestPayloadType();
|
ResolvableType requestPayloadType = getRequestPayloadType();
|
||||||
Class<?> expectedType;
|
|
||||||
if (requestPayloadType == null) {
|
if (requestPayloadType == null) {
|
||||||
expectedType = "text".equals(contentType.getType()) ? String.class : byte[].class;
|
requestPayloadType =
|
||||||
}
|
ResolvableType.forClass(
|
||||||
else {
|
"text".equals(contentType.getType())
|
||||||
expectedType = requestPayloadType.resolve();
|
? String.class
|
||||||
|
: byte[].class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
Type targetType = requestPayloadType.getType();
|
||||||
* TODO: resolve() can return null, which is not valid for canRead().
|
Class<?> targetClass = requestPayloadType.toClass();
|
||||||
* Perhaps we should coerce to String/byte[] instead of attempting
|
|
||||||
* to convert. However this might be a breaking change - 5.2?
|
|
||||||
* Hence NOSONAR below.
|
|
||||||
*/
|
|
||||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||||
if (converter.canRead(expectedType, contentType)) {
|
GenericHttpMessageConverter<?> genericConverter =
|
||||||
return converter.read((Class) expectedType, request);
|
converter instanceof GenericHttpMessageConverter
|
||||||
|
? (GenericHttpMessageConverter<?>) converter
|
||||||
|
: null;
|
||||||
|
if (genericConverter != null
|
||||||
|
? genericConverter.canRead(targetType, null, contentType) :
|
||||||
|
(converter.canRead(targetClass, contentType))) {
|
||||||
|
|
||||||
|
if (genericConverter != null) {
|
||||||
|
return genericConverter.read(targetType, null, request);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return converter.read((Class) targetClass, request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new MessagingException(// NOSONAR might be null; see comment above.
|
throw new MessagingException(
|
||||||
"Could not convert request: no suitable HttpMessageConverter found for expected type ["
|
"Could not convert request: no suitable HttpMessageConverter found for expected type ["
|
||||||
+ expectedType != null ? expectedType.getName() : "null"
|
+ requestPayloadType + "] and content type [" + contentType + "]");
|
||||||
+ "] and content type [" + contentType + "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,10 +44,11 @@ import org.springframework.web.HttpRequestHandler;
|
|||||||
* (e.g. 200 OK).
|
* (e.g. 200 OK).
|
||||||
* <p>
|
* <p>
|
||||||
* The default supported request methods are GET and POST, but the list of values can be configured with the
|
* The default supported request methods are GET and POST, but the list of values can be configured with the
|
||||||
* {@link RequestMapping#methods} property. The payload generated from a GET request (or HEAD or OPTIONS if supported) will
|
* {@link RequestMapping#getMethods()} property.
|
||||||
* be a {@link org.springframework.util.MultiValueMap} containing the parameter values. For a request containing a body
|
* The payload generated from a GET request (or HEAD or OPTIONS if supported) will
|
||||||
* (e.g. a POST), the type
|
* be a {@link org.springframework.util.MultiValueMap} containing the parameter values.
|
||||||
* of the payload is determined by the {@link #setRequestPayloadTypeClass(Class)} request payload type}.
|
* For a request containing a body (e.g. a POST), the type of the payload is determined
|
||||||
|
* by the {@link #setRequestPayloadTypeClass(Class)} request payload type}.
|
||||||
* <p>
|
* <p>
|
||||||
* If the HTTP request is a multipart and a "multipartResolver" bean has been defined in the context, then it will be
|
* If the HTTP request is a multipart and a "multipartResolver" bean has been defined in the context, then it will be
|
||||||
* converted by the
|
* converted by the
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.integration.http.inbound;
|
package org.springframework.integration.http.inbound;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
@@ -38,6 +39,8 @@ import org.hamcrest.Matchers;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.expression.common.LiteralExpression;
|
import org.springframework.expression.common.LiteralExpression;
|
||||||
import org.springframework.http.HttpInputMessage;
|
import org.springframework.http.HttpInputMessage;
|
||||||
import org.springframework.http.HttpOutputMessage;
|
import org.springframework.http.HttpOutputMessage;
|
||||||
@@ -64,6 +67,8 @@ import org.springframework.util.LinkedMultiValueMap;
|
|||||||
import org.springframework.util.SerializationUtils;
|
import org.springframework.util.SerializationUtils;
|
||||||
import org.springframework.web.multipart.MultipartResolver;
|
import org.springframework.web.multipart.MultipartResolver;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Gary Russell
|
* @author Gary Russell
|
||||||
@@ -274,9 +279,52 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
|||||||
assertEquals(TestBean.class, message.getPayload().getClass());
|
assertEquals(TestBean.class, message.getPayload().getClass());
|
||||||
TestBean result = (TestBean) message.getPayload();
|
TestBean result = (TestBean) message.getPayload();
|
||||||
assertEquals("T. Bean", result.name);
|
assertEquals("T. Bean", result.name);
|
||||||
assertEquals(84, result.age);
|
assertEquals(42, result.age);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJsonRequestBody() throws Exception {
|
||||||
|
QueueChannel channel = new QueueChannel();
|
||||||
|
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false);
|
||||||
|
gateway.setBeanFactory(mock(BeanFactory.class));
|
||||||
|
ParameterizedTypeReference<List<TestBean>> parameterizedTypeReference =
|
||||||
|
new ParameterizedTypeReference<List<TestBean>>() {
|
||||||
|
|
||||||
|
};
|
||||||
|
gateway.setRequestPayloadType(ResolvableType.forType(parameterizedTypeReference));
|
||||||
|
gateway.setRequestChannel(channel);
|
||||||
|
gateway.afterPropertiesSet();
|
||||||
|
gateway.start();
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test");
|
||||||
|
|
||||||
|
request.setContentType("application/json");
|
||||||
|
TestBean testBean = new TestBean();
|
||||||
|
testBean.setName("T. Bean");
|
||||||
|
testBean.setAge(42);
|
||||||
|
request.setContent(new ObjectMapper().writeValueAsBytes(new TestBean[] { testBean }));
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
gateway.handleRequest(request, response);
|
||||||
|
byte[] bytes = response.getContentAsByteArray();
|
||||||
|
assertThat(bytes).isNotNull();
|
||||||
|
Message<?> message = channel.receive(0);
|
||||||
|
|
||||||
|
assertThat(message).isNotNull()
|
||||||
|
.extracting(Message::getPayload)
|
||||||
|
.isInstanceOf(List.class)
|
||||||
|
.asList()
|
||||||
|
.hasSize(1)
|
||||||
|
.element(0)
|
||||||
|
.isInstanceOf(TestBean.class)
|
||||||
|
.satisfies((actual) -> {
|
||||||
|
TestBean bean = (TestBean) actual;
|
||||||
|
assertThat(bean).extracting(TestBean::getName).isEqualTo("T. Bean");
|
||||||
|
assertThat(bean).extracting(TestBean::getAge).isEqualTo(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void INT2680DuplicateContentTypeHeader() throws Exception {
|
public void INT2680DuplicateContentTypeHeader() throws Exception {
|
||||||
|
|
||||||
@@ -496,7 +544,15 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setAge(int age) {
|
public void setAge(int age) {
|
||||||
this.age = age * 2;
|
this.age = age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAge() {
|
||||||
|
return age;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user