跳到主要内容

Web 服务支持

QWen Plus 中英对照 Web Services Support

本章描述了 Spring Integration 对 web 服务的支持,包括:

你需要将这个依赖添加到你的项目中:

<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ws</artifactId>
<version>6.4.2</version>
</dependency>
xml

外发 Web 服务网关

当你向通道发送消息时,调用 Web 服务有两种选择,这两种都建立在 Spring Web Services 项目之上:SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway。前者接受 Stringjavax.xml.transform.Source 作为消息的有效负载。后者支持 MarshallerUnmarshaller 接口的任何实现。两者都需要一个 Spring Web Services DestinationProvider,以确定要调用的 Web 服务的 URI。以下示例显示了调用 Web 服务的两种选项:

simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);

marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
java
备注

在使用命名空间支持 (稍后描述) 时,您只需要设置一个 URI。内部,解析器配置了一个固定的 URI DestinationProvider 实现。但是,如果您需要在运行时动态解析 URI,则 DestinationProvider 可以提供诸如从注册表查找 URI 的行为。有关此策略的更多信息,请参阅 Spring Web Services DestinationProvider Javadoc。

从 5.0 版本开始,您可以为 SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway 提供一个外部的 WebServiceTemplate 实例,您可以为其配置任何自定义属性,包括 checkConnectionForFault(这使您的应用程序能够处理不符合规范的服务)。

对于内部工作原理的更多详细信息,请参阅 Spring Web Services 参考指南中关于 客户端访问 的章节和关于 Object/XML 映射 的章节。

入站 Web 服务网关

在接收到 web 服务调用时,要向通道发送消息,你有两个选择:SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway。前者从 WebServiceMessage 中提取一个 javax.xml.transform.Source 并将其设置为消息的有效负载。后者支持实现 MarshallerUnmarshaller 接口。如果传入的 web 服务消息是 SOAP 消息,则会将 SOAP 操作头添加到转发到请求通道的 Message 的头中。以下示例显示了这两种选项:

The following example shows both options:

<int-ws:inbound-gateway id="simpleWebServiceInboundGateway"
request-channel="requestChannel" />

<int-ws:inbound-gateway id="marshallingWebServiceInboundGateway"
request-channel="requestChannel"
marshaller="marshaller"
unmarshaller="unmarshaller" />
xml
simpleGateway = new SimpleWebServiceInboundGateway();
simpleGateway.setRequestChannel(forwardOntoThisChannel);
simpleGateway.setReplyChannel(listenForResponseHere); //Optional

marshallingGateway = new MarshallingWebServiceInboundGateway(marshaller);
//set request and optionally reply channel
java

两个网关都实现了 Spring Web Services MessageEndpoint 接口,因此可以使用 MessageDispatcherServlet 进行配置,遵循标准的 Spring Web Services 配置。

有关如何使用这些组件的更多详细信息,请参阅 Spring Web Services 参考指南中关于 创建 Web 服务 的章节。涵盖 Object/XML 映射 的章节同样适用。

要将 SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway 配置添加到 Spring WS 基础设施中,您应该在 MessageDispatcherServlet 和目标 MessageEndpoint 实现之间添加 EndpointMapping 定义,就像为一个普通的 Spring WS 应用程序所做的那样。为此(从 Spring Integration 的角度来看),Spring WS 提供了以下方便的 EndpointMapping 实现:

  • o.s.ws.server.endpoint.mapping.UriEndpointMapping

  • o.s.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping

  • o.s.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping

  • o.s.ws.server.endpoint.mapping.XPathPayloadEndpointMapping

你必须在应用程序上下文中为这些类指定 bean,并根据 WS 映射算法引用 SimpleWebServiceInboundGateway 和/或 MarshallingWebServiceInboundGateway 的 bean 定义。

有关更多信息,请参阅 端点映射

Web Service 命名空间支持

要配置一个 outbound web 服务网关,使用 ws 命名空间中的 outbound-gateway 元素,如下例所示:

<int-ws:outbound-gateway id="simpleGateway"
request-channel="inputChannel"
uri="https://example.org"/>
xml
备注

此示例未提供 'reply-channel'。如果 Web 服务返回非空响应,则包含该响应的 Message 将发送到请求消息的 REPLY_CHANNEL 标头中定义的回复通道。如果该通道不可用,则会抛出通道解析异常。如果您想将回复发送到其他通道,请在 'outbound-gateway' 元素上提供 'reply-channel' 属性。

提示

默认情况下,当你调用一个在使用字符串有效负载请求 Message 后返回空响应的 Web 服务时,不会发送回复 Message。因此,你不需要设置 'reply-channel' 或在请求 Message 中拥有 REPLY_CHANNEL 标头。如果你确实希望将空响应作为 Message 接收,可以将 'ignore-empty-responses' 属性设置为 false。这样做仅适用于 String 对象,因为使用 SourceDocument 对象会导致空响应,从而永远不会生成回复 Message

要设置入站 Web 服务网关,请使用 inbound-gateway 元素,如下例所示:

<int-ws:inbound-gateway id="simpleGateway"
request-channel="inputChannel"/>
xml

要使用 Spring OXM 的序列化程序或反序列化程序,您必须提供 bean 引用。以下示例展示了如何为传出序列化网关提供 bean 引用:

<int-ws:outbound-gateway id="marshallingGateway"
request-channel="requestChannel"
uri="https://example.org"
marshaller="someMarshaller"
unmarshaller="someUnmarshaller"/>
xml

以下示例展示了如何为入站 marshalling 网关提供 bean 引用:

<int-ws:inbound-gateway id="marshallingGateway"
request-channel="requestChannel"
marshaller="someMarshaller"
unmarshaller="someUnmarshaller"/>
xml
备注

大多数 Marshaller 实现也实现了 Unmarshaller 接口。当使用此类 Marshaller 时,仅需要 marshaller 属性。即使在使用 Marshaller 的情况下,也可以为 outbound gateways 上的 request-callback 提供引用。

对于任一出站网关类型,你可以指定一个 destination-provider 属性而不是 uri(它们之中恰好需要一个)。然后你可以引用任何 Spring Web Services DestinationProvider 实现(例如,在运行时从注册表查找 URI)。

对于任一 outbound 网关类型,message-factory 属性也可以配置为引用任何 Spring Web Services WebServiceMessageFactory 实现。

对于简单的入站网关类型,你可以将 extract-payload 属性设置为 false,以转发整个 WebServiceMessage 而不仅仅是其有效负载作为 Message 到请求通道。这样做可能很有用,例如,当自定义转换器直接针对 WebServiceMessage 进行操作时。

从 5.0 版本开始,web-service-template 引用属性允许您注入带有任何可能自定义属性的 WebServiceTemplate

Web Service Java DSL 支持

Web Service Namespace Support 中显示的网关的等效配置如下所示:

@Bean
IntegrationFlow inbound() {
return IntegrationFlow.from(Ws.simpleInboundGateway()
.id("simpleGateway"))
...
.get();
}
java
@Bean
IntegrationFlow outboundMarshalled() {
return f -> f.handle(Ws.marshallingOutboundGateway()
.id("marshallingGateway")
.marshaller(someMarshaller())
.unmarshaller(someUnmarshalller()))
...
}
java
@Bean
IntegrationFlow inboundMarshalled() {
return IntegrationFlow.from(Ws.marshallingInboundGateway()
.marshaller(someMarshaller())
.unmarshaller(someUnmarshalller())
.id("marshallingGateway"))
...
.get();
}
java

其他属性可以以流畅的方式设置在端点规范上(属性取决于是否为 outbound 网关提供了一个外部 WebServiceTemplate)。示例:

.from(Ws.simpleInboundGateway()
.extractPayload(false))
java
.handle(Ws.simpleOutboundGateway(template)
.uri(uri)
.sourceExtractor(sourceExtractor)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.NONE)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions)
.extractPayload(false))
)
java
.handle(Ws.marshallingOutboundGateway()
.destinationProvider(destinationProvider)
.marshaller(marshaller)
.unmarshaller(unmarshaller)
.messageFactory(messageFactory)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY)
.faultMessageResolver(faultMessageResolver)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.interceptors(interceptor)
.messageSenders(messageSender)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions))
java
.handle(Ws.marshallingOutboundGateway(template)
.uri(uri)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions))
)
java

出站 URI 配置

对于 Spring Web Services 支持的所有 URI 方案(参见 URI 和传输),提供了 <uri-variable/> 替换。以下示例展示了如何定义它:

<ws:outbound-gateway id="gateway" request-channel="input"
uri="https://springsource.org/{thing1}-{thing2}">
<ws:uri-variable name="thing1" expression="payload.substring(1,7)"/>
<ws:uri-variable name="thing2" expression="headers.x"/>
</ws:outbound-gateway>

<ws:outbound-gateway request-channel="inputJms"
uri="jms:{destination}?deliveryMode={deliveryMode}&amp;priority={priority}"
message-sender="jmsMessageSender">
<ws:uri-variable name="destination" expression="headers.jmsQueue"/>
<ws:uri-variable name="deliveryMode" expression="headers.deliveryMode"/>
<ws:uri-variable name="priority" expression="headers.jms_priority"/>
</ws:outbound-gateway>
xml

如果你提供了 DestinationProvider,则不支持变量替换,如果你提供变量,则会发生配置错误。

控制 URI 编码

默认情况下,URL 字符串在发送请求之前会被编码为 URI 对象(参见 UriComponentsBuilder)。在某些非标准 URI 的场景中,执行编码是不希望的。<ws:outbound-gateway/> 元素提供了一个 encoding-mode 属性。要禁用 URL 编码,将此属性设置为 NONE(默认情况下,它是 TEMPLATE_AND_VALUES)。如果您希望部分编码 URL 的某些部分,可以通过在 <uri-variable/> 中使用 expression 来实现,如下例所示:

<ws:outbound-gateway url="https://somehost/%2f/fooApps?bar={param}" encoding-mode="NONE">
<http:uri-variable name="param"
expression="T(org.apache.commons.httpclient.util.URIUtil)
.encodeWithinQuery('Hello World!')"/>
</ws:outbound-gateway>
xml
备注

如果你设置了 DestinationProvider,则忽略 encoding-mode

WS 消息头

Spring Integration 的 Web 服务网关会自动映射 SOAP 操作头。默认情况下,它通过使用 DefaultSoapHeaderMapper 复制到和来自 Spring Integration MessageHeaders

你可以传入自己实现的 SOAP 特定的头映射器,因为网关具有支持这样做的属性。

除非由 DefaultSoapHeaderMapperrequestHeaderNamesreplyHeaderNames 属性明确指定,否则任何用户定义的 SOAP 头不会被复制到或从 SOAP 消息中。

当你使用 XML 命名空间进行配置时,你可以通过使用 mapped-request-headersmapped-reply-headers 属性来设置这些属性,你还可以通过设置 header-mapper 属性来提供自定义映射器。

提示

在映射用户定义的标头时,值也可以包含简单的通配符模式(例如 myheader***myheader**)。例如,如果您需要复制所有用户定义的标头,可以使用通配符字符:**。

从 4.1 版开始,AbstractHeaderMapperDefaultSoapHeaderMapper 的父类)允许为 requestHeaderNamesreplyHeaderNames 属性配置 NON_STANDARD_HEADERS 标记(除了已有的 STANDARD_REQUEST_HEADERSSTANDARD_REPLY_HEADERS),以映射所有用户定义的头信息。

备注

我们建议使用以下组合,而不是使用通配符(*):STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS。这样做可以避免将 request 头映射到回复中。

从 4.3 版本开始,您可以通过在模式前加上 ! 来否定标头映射中的模式。否定的模式具有优先权,因此像 STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1 这样的列表不会映射 thing1thing2thing3。它确实会映射标准标头、thing4qux。(请注意,thing1 同时包含在非否定和否定形式中。由于否定值具有优先权,thing1 不会被映射。)

important

如果你有一个以 ! 开头的自定义标题需要映射,你可以使用 \ 来转义它,如下所示:STANDARD_REQUEST_HEADERS,\!myBangHeader。然后 !myBangHeader 将被映射。

入站 SOAP 头(入站网关的请求头和出站网关的回复头)被映射为 SoapHeaderElement 对象。您可以通过访问 Source 来查看内容:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<auth>
<username>user</username>
<password>pass</password>
</auth>
<bar>BAR</bar>
<baz>BAZ</baz>
<qux>qux</qux>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
xml

如果 mapped-request-headersauth, ca*,那么 authcatcan 头将被映射,但 qux 不会被映射。

以下示例展示了如何从名为 auth 的header中获取一个名为 user 的值:

...
SoapHeaderElement header = (SoapHeaderElement) headers.get("auth");
DOMSource source = (DOMSource) header.getSource();
NodeList nodeList = source.getNode().getChildNodes();
assertEquals("username", nodeList.item(0).getNodeName());
assertEquals("user", nodeList.item(0).getFirstChild().getNodeValue());
...
java

从 5.0 版本开始,DefaultSoapHeaderMapper 支持用户定义的类型为 javax.xml.transform.Source 的头部,并将它们作为 <soapenv:Header> 的子节点填充。以下示例展示了如何操作:

Map<String, Object> headers = new HashMap<>();

String authXml =
"<auth xmlns='http://test.auth.org'>"
+ "<username>user</username>"
+ "<password>pass</password>"
+ "</auth>";
headers.put("auth", new StringSource(authXml));
...
DefaultSoapHeaderMapper mapper = new DefaultSoapHeaderMapper();
mapper.setRequestHeaderNames("auth");
java

前面示例的结果是以下 SOAP 包装:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<auth xmlns="http://test.auth.org">
<username>user</username>
<password>pass</password>
</auth>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
xml

MTOM 支持

编排入站和出站 Web 服务网关通过编排器的内置功能直接支持附件(例如,Jaxb2Marshaller 提供 mtomEnabled 选项)。从 5.0 版开始,简单的 Web 服务网关可以直接操作入站和出站 MimeMessage 实例,这些实例具有操作附件的 API。当您需要发送带有附件的 Web 服务消息(无论是来自服务器的回复还是客户端请求)时,您应该直接使用 WebServiceMessageFactory 并将带有附件的 WebServiceMessage 作为 payload 发送到网关的请求或回复通道。以下示例展示了如何做到这一点:

WebServiceMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
MimeMessage webServiceMessage = (MimeMessage) messageFactory.createWebServiceMessage();

String request = "<test>foo</test>";

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new StringSource(request), webServiceMessage.getPayloadResult());

webServiceMessage.addAttachment("myAttachment", new ByteArrayResource("my_data".getBytes()), "plain/text");

this.webServiceChannel.send(new GenericMessage<>(webServiceMessage));
java