【问题标题】:Unable to set Reply Channel to Message Header in Spring Integration无法在 Spring 集成中将回复通道设置为消息头
【发布时间】:2019-09-10 03:18:51
【问题描述】:

肯定有一些我一定错过的东西,所以在这里寻求帮助。

我正在使用 Spring Integration 和以下 HTTP 入站网关构建一个简单的 REST 应用程序:

<!-- Gateway -->
    <int-http:inbound-gateway id="fruitQuotePOSTGateway"
                              request-channel="fruitQuotePOSTRequests"
                              supported-methods="POST"
                              path="/api/v1/fruit-quote"
                              request-payload-type="java.lang.String"
                              reply-timeout="10000"
                              reply-channel="fruitQuotePOSTResponses"
                              error-channel="applicationErrors">
        <int-http:request-mapping consumes="application/xml" produces="application/xml"/>
    </int-http:inbound-gateway>

一旦一个 XML 进入这个网关,它会经历以下简单的步骤:

  • 转换以生成与传入请求对应的 JAXB 对象
  • 从 JAXB 对象读取“uuid”并将其设置为 SI(Spring 集成)消息的标头的消息标头丰富
  • 转换以生成对调用客户端的 XML 响应。

首先,这是整个应用程序的 XML 配置(为简洁起见,省略了 HTTP 命名空间):

<!-- Gateway -->
    <int-http:inbound-gateway id="fruitQuotePOSTGateway"
                              request-channel="fruitQuotePOSTRequests"
                              supported-methods="POST"
                              path="/api/v1/fruit-quote"
                              request-payload-type="java.lang.String"
                              reply-timeout="10000"
                              reply-channel="fruitQuotePOSTResponses"
                              error-channel="applicationErrors">
        <int-http:request-mapping consumes="application/xml" produces="application/xml"/>
    </int-http:inbound-gateway>

    <!--
    - Generate fruit quote request JAXB from the incoming request
    - Create a header "requestUUID" by reading it from fruit quote request JAXB
    - Generate fruit quote acknowledgement response for the calling client
    -->

    <int:transformer input-channel="fruitQuotePOSTRequests"
               ref="fruitQuoteTransformation"
               method="generateFruitQuoteRequestJAXB"/>

    <int:header-enricher input-channel="requestUUIDEnrichment" output-channel="orderIDGeneration">
        <int:header name="requestUUID" expression="payload.getFruitQuoteRequestJAXB().getFRUITQUOTEREQUESTDATA().getUuid()"/>
    </int:header-enricher>

    <int:transformer input-channel="fruitQuoteAcknowledgementGeneration"
                     ref="fruitQuoteTransformation"
                     method="generateFruitQuoteAcknowledgement"
                     output-channel="fruitQuotePOSTResponses"/>

    <!-- Error handling -->
    <int:transformer input-channel="applicationErrors"
                     ref="fruitQuoteTransformation"
                     method="generateFruitQuoteAcknowledgementWithError"
                     output-channel="fruitQuotePOSTResponses"/>

    <!-- Channels -->
    <int:channel id="fruitQuotePOSTRequests"/>
    <int:channel id="requestUUIDEnrichment"/>
    <int:channel id="fruitQuotePOSTResponses"/>
    <int:channel id="fruitQuoteAcknowledgementGeneration"/>
    <int:channel id="applicationErrors"/>

应用程序中从一个步骤流向另一个步骤的payload是一个自定义的Builder Object,如下(省略包名):

import static java.util.Objects.nonNull;

public class FruiteQuoteComposite {
    private final FRUITQUOTEREQUEST fruitQuoteRequestJAXB;
    private final FruitQuoteApplicationException fruitQuoteApplicationException;
    private final Integer orderID;
    private final ErrorInformation errorInformation;

    private FruiteQuoteComposite(FruiteQuoteCompositeBuilder fruiteQuoteCompositeBuilder) {
        this.fruitQuoteRequestJAXB = fruiteQuoteCompositeBuilder.fruitQuoteRequestJAXB;
        this.fruitQuoteApplicationException = fruiteQuoteCompositeBuilder.fruitQuoteApplicationException;
        this.orderID = fruiteQuoteCompositeBuilder.orderID;
        this.errorInformation = fruiteQuoteCompositeBuilder.errorInformation;
    }

    public FruitQuoteApplicationException getFruitQuoteApplicationException() {
        return fruitQuoteApplicationException;
    }

    public FRUITQUOTEREQUEST getFruitQuoteRequestJAXB() {
        return fruitQuoteRequestJAXB;
    }

    public Integer getOrderID() {
        return orderID;
    }

    public ErrorInformation getErrorInformation() {
        return errorInformation;
    }

    public static class FruiteQuoteCompositeBuilder {
        private FRUITQUOTEREQUEST fruitQuoteRequestJAXB;
        private FruitQuoteApplicationException fruitQuoteApplicationException;
        private Integer orderID;
        private ErrorInformation errorInformation;

        public FruiteQuoteCompositeBuilder() {
        }

        public FruiteQuoteCompositeBuilder setFruitQuoteRequestJAXB(FRUITQUOTEREQUEST fruitQuoteRequestJAXB) {
            if (nonNull(fruitQuoteRequestJAXB)) {
                this.fruitQuoteRequestJAXB = fruitQuoteRequestJAXB;
            }

            return this;
        }

        public FruiteQuoteCompositeBuilder setFruitQuoteApplicationException(FruitQuoteApplicationException fruitQuoteApplicationException) {
            if (nonNull(fruitQuoteApplicationException)) {
                this.fruitQuoteApplicationException = fruitQuoteApplicationException;
            }

            return this;
        }

        public FruiteQuoteCompositeBuilder setOrderID(Integer orderID) {
            if(nonNull(orderID)) {
                this.orderID = orderID;
            }

            return this;
        }

        public FruiteQuoteCompositeBuilder setErrorInformation(ErrorInformation errorInformation) {
            if (nonNull( errorInformation )) {
                this.errorInformation = errorInformation;
            }
            return this;
        }

        public FruiteQuoteComposite build() {
            return new FruiteQuoteComposite(this);
        }
    }
}

我没有在转换器上使用“输出通道”的原因是因为我想在运行转换的 java 逻辑中明确选择 replyChannel/outgoing 路由。

例如,在 FruitQuoteTransformation.generateFruitQuoteRequestJAXB 方法中,我设置了一条成功路径和另一条异常/错误路径,如下所示:

public Message<FruiteQuoteComposite> generateFruitQuoteRequestJAXB(Message<String> fruitQuoteRequestMessage) {
        String fruitQuoteRequest = fruitQuoteRequestMessage.getPayload();
        Unmarshaller unmarshaller;
        FRUITQUOTEREQUEST fruitQuoteRequestJAXB;

        try {
            unmarshaller = requireNonNull(fruitQuoteRequestJaxbContext).createUnmarshaller();
            fruitQuoteRequestJAXB = (FRUITQUOTEREQUEST) requireNonNull(unmarshaller)
                    .unmarshal(new StringReader(fruitQuoteRequest));
        } catch (JAXBException jaxbException) {
            logger.error("JAXB Unmarshalling exception occurred with error code :: " + ERR_FRUIT_QUOTE_REQUEST_JAXB_TRANSFORMATION, jaxbException);
            FruitQuoteApplicationException fruitQuoteApplicationException = generateFruitQuoteApplicationException(ERR_FRUIT_QUOTE_REQUEST_JAXB_TRANSFORMATION, MESSAGE_FRUIT_QUOTE_INTERNAL_SYSTEM_ERROR);

            FruiteQuoteComposite outboundFruitQuoteComposite = new FruiteQuoteComposite.FruiteQuoteCompositeBuilder()
                    .setFruitQuoteApplicationException(fruitQuoteApplicationException)
                    .build();

            return withPayload(requireNonNull(outboundFruitQuoteComposite))
                    .setHeader(MessageHeaders.REPLY_CHANNEL, "applicationErrors")
                    .build();
        }

        FruiteQuoteComposite outboundFruitQuoteComposite = new FruiteQuoteComposite.FruiteQuoteCompositeBuilder()
                .setFruitQuoteRequestJAXB(fruitQuoteRequestJAXB)
                .build();

        return withPayload(requireNonNull(outboundFruitQuoteComposite))
                .setHeader(MessageHeaders.REPLY_CHANNEL, "requestUUIDEnrichment")
                .build();
    }
  • 我的第一个问题 出于某种原因,.setHeader 调用没有按预期工作,并且消息不会发送到下一个频道。有什么我想念的吗?即使我使用 .setReplyChannelName,结果也是一样的。
  • 我的第二个问题 如果问题 1) 有解决方案,将整体 SI 配置保持为基于 XML,是否有另一种方法来设置自定义回复通道?我想到的唯一选择是在每个变压器之后使用路由器,但这似乎太冗长了。

你能帮忙吗?

【问题讨论】:

    标签: spring-integration spring-integration-dsl spring-integration-http


    【解决方案1】:

    你绝不能乱用框架的replyChannel 标头;它不用于路由目的。

    replyChannel 是一个内部通道,对于用于关联对请求的回复的每条消息都是唯一的。在入站网关上通常不需要显式的reply-channel;如果这样做,它只是在运行时桥接到消息的 replyChannel 标头。

    应该在网关的error-channel 上处理错误情况,而不是通过抛出异常来处理。不同的异常类型可以指示不同的错误。

    【讨论】:

    • 哦,好吧 - 这对我来说是一个重要的学习!使用您使用最近网关的错误通道作为所有异常的安全网的想法,为了实现这一点,是否可以简单地抛出应用程序级异常(从任何适用的转换器/服务激活器方法)并期望它们作为 Message 进入网关的错误通道,并将 MessagingException 有效负载存储为“原因”的底层应用程序特定异常?
    • 你的理解是正确的。确实,您的例外将作为原因出现
    • 谢谢阿特姆!我将执行此操作并回发。
    • 我在这里得到了答案:stackoverflow.com/questions/31778650/… 给定“Message messingExceptionMessage”,messagingExceptionMessage.getPayload().getFailedMessage().getHeaders() 产生了失败消息的所有标头!跨度>
    • 您可以更快地搜索和阅读(并在此处回复),这样我就可以为您准备答案了 ;-)
    猜你喜欢
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-23
    • 2022-01-15
    相关资源
    最近更新 更多