I have IntegrationFlow
where I call HTTP endpoint:
@Bean
public IntegrationFlow getInformationFlow(RestTemplate restTemplate) {
return IntegrationFlows.from(GET_RESPONSE_ENTITY)
.handle(Http
.outboundGateway(url + "/application/{code}", restTemplate)
.httpMethod(GET)
.uriVariable("code", "payload")
.expectedResponseType(new ParameterizedTypeReference<ResponseEntity<Information>>() {
})
).get();
}
This flow is executed when getInformation
is called thanks to Gateway
@MessagingGateway
public interface MyGateway {
@Gateway(requestChannel = GET_RESPONSE_ENTITY)
ResponseEntity<Information> getInformation(String code);
}
Code above throws following exception:
Caused by: org.springframework.http.converter.HttpMessageConversionException:
Type definition error: [simple type, class org.springframework.http.ResponseEntity];
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of `org.springframework.http.ResponseEntity`
(no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Jackson tries to deserialize into ResponseEntity
instead of Information
class
My goal is to check status code first and optionally check Information
fields
Is it possible to return ResponseEntity from method annotated by @Gateway
?
Short answer to your question: it depends on that MyGateway
contract implementation. It is really not a gateway (or any interface API) responsibility to deal with returns. They define only contracts. It is already your goal to implement such a contract properly.
What I mean by this that Spring Integration with its EIP components does not go further than regular Java program design and architecture. It is just a particular case that this contract is an IntegrationFlow
as an implementation. Therefore the problem is not with a contract, but rather implementation details, which is an HTTP call in your case.
So, better to ask the question like:
How to return a
ResponseEntity<Information>
from theHttp.outboundGateway()
with a Jackson message converter?
That's why I asked you on Gitter for this SO thread to better understand what is going on. Your original question is misleading and really has nothing to do with a @MessagingGateway
. I'm even sure that is some clue in the stack trace that the problem happens on the RestTemplate
call, not in the @MessagingGateway
.
Now trying to help you to fix your explicit problem.
The AbstractHttpRequestExecutingMessageHandler
does not return a ResponseEntity
when there is a body
:
protected Object getReply(ResponseEntity<?> httpResponse) {
HttpHeaders httpHeaders = httpResponse.getHeaders();
Map<String, Object> headers = this.headerMapper.toHeaders(httpHeaders);
if (this.transferCookies) {
doConvertSetCookie(headers);
}
AbstractIntegrationMessageBuilder<?> replyBuilder;
MessageBuilderFactory messageBuilderFactory = getMessageBuilderFactory();
if (httpResponse.hasBody()) {
Object responseBody = httpResponse.getBody();
replyBuilder = (responseBody instanceof Message<?>)
? messageBuilderFactory.fromMessage((Message<?>) responseBody)
: messageBuilderFactory.withPayload(responseBody); // NOSONAR - hasBody()
}
else {
replyBuilder = messageBuilderFactory.withPayload(httpResponse);
}
replyBuilder.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE,
httpResponse.getStatusCode());
return replyBuilder.copyHeaders(headers);
}
Only if there is no body. In that case it means there is nothing to map into a payload
for the reply message, therefore we use the whole ResponseEntity
.
As you see in this code snippet, the StatusCode
is mapped to the org.springframework.integration.http.HttpHeaders.STATUS_CODE
reply message header.
There is also some explanation in docs on the matter: https://docs.spring.io/spring-integration/docs/current/reference/html/http.html#using-cookies.
From here it means that your expectedResponseType
can be only as an Information
type. The RestTemplate
with its HttpMessageConverts
indeed doesn't know what to do with a ResponseEntity
type to map.
Since it may return just an Information
in the payload if it comes in the response, or may return the whole ResponseEntity
with an empty body, it looks like you have to add some routing and transformation logic before returning a ResponseEntity<Information>
as a reply to your @MessagingGateway
call. Or you can revise that gateway contract and really implement a status code check in the integration flow via router or filter - and your @MessagingGateway
consumer would be free from the HTTP stuff like status code checking and headers conversion.
Although it might not hurt to have some option on the AbstractHttpRequestExecutingMessageHandler
to always return the whole ResponseEntity
as a payload. Feel free to raise a GH issue a we will consider to implement it sooner than later!