首页 > 解决方案 > 从 Spring Integration 的 @Gateway 方法返回 ResponseEntity

问题描述

我有IntegrationFlow我称之为 HTTP 端点的地方:

@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();
}

此流程在getInformation被调用时执行,这要归功于Gateway

@MessagingGateway
public interface MyGateway {

    @Gateway(requestChannel = GET_RESPONSE_ENTITY)
    ResponseEntity<Information> getInformation(String code);

}

上面的代码抛出以下异常:

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)

杰克逊试图反序列化成ResponseEntity而不是Information

我的目标是首先检查状态代码,并可选择检查Information字段

是否可以从注释的方法返回 ResponseEntity @Gateway

标签: spring-integrationspring-integration-http

解决方案


对您的问题的简短回答:这取决于该MyGateway合同的实施。处理返回实际上不是网关(或任何接口 API)的责任。他们只定义合同。正确执行这样的合同已经是您的目标。

我的意思是 Spring Integration 及其 EIP 组件并没有比常规的 Java 程序设计和架构更进一步。这只是一个特例,这个合约是IntegrationFlow一个实现。因此,问题不在于合同,而在于实施细节,在您的情况下这是一个 HTTP 调用。

所以,最好问这样的问题:

如何ResponseEntity<Information>Http.outboundGateway()带有杰克逊消息转换器的返回一个?

这就是为什么我在 Gitter 上问你这个 SO 线程以更好地了解发生了什么。您最初的问题具有误导性,并且与@MessagingGateway. 我什至确信堆栈跟踪中有一些线索表明问题发生在RestTemplate调用上,而不是在@MessagingGateway.

现在试图帮助您解决您的明确问题。

有a时AbstractHttpRequestExecutingMessageHandler不返回a :ResponseEntitybody

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);
}

只有在没有身体的情况下。在这种情况下,这意味着没有任何内容可以映射到payload回复消息中,因此我们使用整个ResponseEntity.

正如您在此代码片段中看到的,StatusCode被映射到org.springframework.integration.http.HttpHeaders.STATUS_CODE回复消息标头。

关于此事的文档中也有一些解释:https ://docs.spring.io/spring-integration/docs/current/reference/html/http.html#using-cookies 。

从这里开始,这意味着您expectedResponseType只能作为一种Information类型。RestTemplatewith HttpMessageConvertsit 确实不知道如何处理ResponseEntity要映射的类型。

由于它可能Information在响应中返回有效负载中的一个,或者可能返回ResponseEntity一个空主体的整体,看起来您必须添加一些路由和转换逻辑,然后再返回一个ResponseEntity<Information>作为对@MessagingGateway调用的回复。或者您可以修改该网关合同并通过路由器或过滤器在集成流中真正实施状态代码检查 - 您的@MessagingGateway消费者将摆脱 HTTP 的东西,如状态代码检查和标头转换。

尽管有一些选项AbstractHttpRequestExecutingMessageHandler总是将整个ResponseEntity作为有效负载返回可能没有什么坏处。随意提出一个 GH 问题,我们将考虑尽快实施它!


推荐阅读