无法在 Spring 集成中将回复频道设置为消息 Header
Unable to set Reply Channel to Message Header in Spring Integration
肯定是我遗漏了什么所以在这里寻求帮助。
我正在使用 Spring 与以下 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 object 对应传入请求
- 消息 header 从 JAXB Object 读取 "uuid" 并将其设置为 SI(Spring 集成)消息的 header 的扩充
- 转换以生成对调用客户端的 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"/>
在应用程序中从一个步骤流向另一个步骤的有效载荷是一个自定义生成器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);
}
}
}
我没有在转换器上使用 "output-channel" 的原因是因为我想在 java 逻辑 运行 转换中明确选择 replyChannel/outgoing 路由.
例如,在 FruitQuoteTransformation.generateFruitQuoteRequestJAXB 方法中,我为成功设置了一条路线,为 exceptions/errors 设置了另一条路线,如下所示:
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-based,是否有替代方法来设置自定义回复通道?我想到的唯一选择是在每个变压器之后使用路由器,但这似乎太冗长了。
你能帮忙吗?
绝对不能乱用框架的replyChannel
header;它不用于路由目的。
replyChannel
是一个内部渠道,每条消息都是唯一的,用于关联对请求的回复。您通常不需要在入站网关上显式 reply-channel
;如果这样做,它会在运行时简单地桥接到消息的 replyChannel header.
应该在网关 error-channel
上处理错误情况,而不是抛出异常。不同的异常类型可以指示不同的错误。
肯定是我遗漏了什么所以在这里寻求帮助。
我正在使用 Spring 与以下 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 object 对应传入请求
- 消息 header 从 JAXB Object 读取 "uuid" 并将其设置为 SI(Spring 集成)消息的 header 的扩充
- 转换以生成对调用客户端的 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"/>
在应用程序中从一个步骤流向另一个步骤的有效载荷是一个自定义生成器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);
}
}
}
我没有在转换器上使用 "output-channel" 的原因是因为我想在 java 逻辑 运行 转换中明确选择 replyChannel/outgoing 路由.
例如,在 FruitQuoteTransformation.generateFruitQuoteRequestJAXB 方法中,我为成功设置了一条路线,为 exceptions/errors 设置了另一条路线,如下所示:
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-based,是否有替代方法来设置自定义回复通道?我想到的唯一选择是在每个变压器之后使用路由器,但这似乎太冗长了。
你能帮忙吗?
绝对不能乱用框架的replyChannel
header;它不用于路由目的。
replyChannel
是一个内部渠道,每条消息都是唯一的,用于关联对请求的回复。您通常不需要在入站网关上显式 reply-channel
;如果这样做,它会在运行时简单地桥接到消息的 replyChannel header.
应该在网关 error-channel
上处理错误情况,而不是抛出异常。不同的异常类型可以指示不同的错误。