|
如需使用最新稳定版本,请使用 Spring Integration 7.0.4! |
Web Services 支持
本章介绍 Spring Integration 对 Web 服务的支持,包括:
您需要将以下依赖项包含到您的项目中:
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ws</artifactId>
<version>6.4.10</version>
</dependency>
compile "org.springframework.integration:spring-integration-ws:6.4.10"
出站 Web 服务网关
当您向通道发送消息以调用 Web 服务时,有两种选项,两者都基于 Spring Web Services 项目:SimpleWebServiceOutboundGateway 和 MarshallingWebServiceOutboundGateway。
前者接受 String 或 javax.xml.transform.Source 作为消息负载。
后者支持任何实现 Marshaller 和 Unmarshaller 接口的类。
两者都需要一个 Spring Web Services DestinationProvider,以确定要调用的 Web 服务的 URI。
以下示例展示了调用 Web 服务的两种选项:
simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);
marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
当使用命名空间支持(后文将描述)时,您只需设置一个 URI。
在内部,解析器会配置一个固定 URI DestinationProvider 实现。
然而,如果您需要在运行时动态解析 URI,那么 DestinationProvider 可以提供此类行为,例如从注册表中查找 URI。
有关此策略的更多信息,请参阅 Spring Web Services 的 DestinationProvider Javadoc。 |
从版本 5.0 开始,您可以使用外部 WebServiceTemplate 实例提供 SimpleWebServiceOutboundGateway 和 MarshallingWebServiceOutboundGateway,该实例可配置任何自定义属性,包括 checkConnectionForFault(允许您的应用程序处理不符合规范的服务)。
入站 Web 服务网关
当接收到 Web 服务调用并向通道发送消息时,您仍然有两个选项:SimpleWebServiceInboundGateway 和 MarshallingWebServiceInboundGateway。
前者从 WebServiceMessage 中提取 javax.xml.transform.Source 并将其设置为消息负载。
后者支持实现 Marshaller 和 Unmarshaller 接口。
如果传入的 Web 服务消息是 SOAP 消息,则 SOAP 操作头将添加到转发到请求通道的 Message 的头中。
以下示例展示了这两种选项:
simpleGateway = new SimpleWebServiceInboundGateway();
simpleGateway.setRequestChannel(forwardOntoThisChannel);
simpleGateway.setReplyChannel(listenForResponseHere); //Optional
marshallingGateway = new MarshallingWebServiceInboundGateway(marshaller);
//set request and optionally reply channel
两个网关都实现了 Spring Web Services MessageEndpoint 接口,因此可以按照标准 Spring Web Services 配置方式,使用 MessageDispatcherServlet 进行配置。
要将 SimpleWebServiceInboundGateway 和 MarshallingWebServiceInboundGateway 配置添加到 Spring WS 基础设施中,您应该像处理普通 Spring WS 应用程序一样,在 MessageDispatcherServlet 和目标 MessageEndpoint 实现之间添加 EndpointMapping 定义。
为此(从 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
您必须在应用程序上下文中为这些类指定 beans,并根据 WS 映射算法引用 SimpleWebServiceInboundGateway 和/或 MarshallingWebServiceInboundGateway bean 定义。
查看 端点映射 以获取更多信息。
Web 服务命名空间支持
要配置出站 Web 服务网关,请使用来自 ws 命名空间的 outbound-gateway 元素,如下例所示:
<int-ws:outbound-gateway id="simpleGateway"
request-channel="inputChannel"
uri="https://example.org"/>
此示例未提供 'reply-channel'。
如果 Web 服务返回非空响应,则包含该响应的 Message 将被发送至请求消息的 REPLY_CHANNEL 头部中定义的回复通道。
如果该通道不可用,则会抛出通道解析异常。
如果您希望将回复发送到另一个通道,请在 'outbound-gateway' 元素上提供 'reply-channel' 属性。 |
默认情况下,当您使用字符串负载发送请求 Message 并调用返回空响应的 Web 服务时,不会发送任何回复 Message。
因此,您无需在请求 Message 中设置 'reply-channel' 或包含 REPLY_CHANNEL 标头。
如果您确实希望将空响应作为 Message 接收,可以将 'ignore-empty-responses' 属性设置为 false。
这样做仅对 String 对象有效,因为使用 Source 或 Document 对象会导致空响应,从而永远不会生成回复 Message。 |
要设置入站 Web 服务网关,请使用 inbound-gateway 元素,如下例所示:
<int-ws:inbound-gateway id="simpleGateway"
request-channel="inputChannel"/>
要使用 Spring OXM 的编组器或解组器,您必须提供 Bean 引用。 以下示例展示了如何为出站编组网关提供 Bean 引用:
<int-ws:outbound-gateway id="marshallingGateway"
request-channel="requestChannel"
uri="https://example.org"
marshaller="someMarshaller"
unmarshaller="someUnmarshaller"/>
以下示例展示了如何为入站编组网关提供 Bean 引用:
<int-ws:inbound-gateway id="marshallingGateway"
request-channel="requestChannel"
marshaller="someMarshaller"
unmarshaller="someUnmarshaller"/>
大多数 Marshaller 实现也会实现 Unmarshaller 接口。
使用此类 Marshaller 时,仅需配置 marshaller 属性即可。
即使在使用 Marshaller 的情况下,您也可以为出站网关提供对 request-callback 的引用。 |
对于任意一种出站网关类型,您可以指定一个 destination-provider 属性来替代 uri(必须且只能指定其中一个)。
随后,您可以引用任何 Spring Web Services DestinationProvider 实现(例如,用于在运行时从注册表中查找 URI)。
对于任意出站网关类型,message-factory属性也可以配置为对任何Spring Web Services WebServiceMessageFactory实现的引用。
对于简单的入站网关类型,您可以将 extract-payload 属性设置为 false,以便将整个 WebServiceMessage(而不仅仅是其负载)作为 Message 转发到请求通道。
这样做可能很有用,例如,当自定义转换器直接针对 WebServiceMessage 工作时。
从 5.0 版本开始,web-service-template 引用属性允许您注入任何可能的自定义属性的 WebServiceTemplate。
Web Service Java DSL 支持
以下代码片段展示了在 Web Service 命名空间支持 中显示的网关的等效配置:
@Bean
IntegrationFlow inbound() {
return IntegrationFlow.from(Ws.simpleInboundGateway()
.id("simpleGateway"))
...
.get();
}
@Bean
IntegrationFlow outboundMarshalled() {
return f -> f.handle(Ws.marshallingOutboundGateway()
.id("marshallingGateway")
.marshaller(someMarshaller())
.unmarshaller(someUnmarshalller()))
...
}
@Bean
IntegrationFlow inboundMarshalled() {
return IntegrationFlow.from(Ws.marshallingInboundGateway()
.marshaller(someMarshaller())
.unmarshaller(someUnmarshalller())
.id("marshallingGateway"))
...
.get();
}
其他属性可以以流畅的方式设置在端点规范上(具体取决于是否为出站网关提供了外部WebServiceTemplate)。
示例:
.from(Ws.simpleInboundGateway()
.extractPayload(false))
.handle(Ws.simpleOutboundGateway(template)
.uri(uri)
.sourceExtractor(sourceExtractor)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.NONE)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions)
.extractPayload(false))
)
.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))
.handle(Ws.marshallingOutboundGateway(template)
.uri(uri)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions))
)
出站 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}&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>
如果您提供 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>
如果您设置为 DestinationProvider,则忽略 encoding-mode。 |
WS 消息头
The Spring Integration web service gateways automatically map the SOAP action header.
By default, it is copied to and from Spring Integration MessageHeaders by using the DefaultSoapHeaderMapper.
您可以传入您自己实现的 SOAP 特定头映射器,因为网关具有支持此操作的属性。
除非 requestHeaderNames 或 replyHeaderNames 属性明确指定,否则任何用户定义的 SOAP 头都不会被复制到 SOAP 消息中,也不会从 SOAP 消息中复制出来。
当您使用 XML 命名空间进行配置时,您可以通过使用 mapped-request-headers 和 mapped-reply-headers 属性来设置这些属性;您还可以通过设置 header-mapper 属性来提供自定义映射器。
当映射用户定义的标头时,值也可以包含简单的通配符模式(例如 myheader* 或 myheader)。
例如,如果您需要复制所有用户定义的标头,可以使用通配符:。 |
从 4.1 版本开始,AbstractHeaderMapper(一个 DefaultSoapHeaderMapper 的超类)允许为 NON_STANDARD_HEADERS Tokens配置 requestHeaderNames 和 replyHeaderNames 属性(除了现有的 STANDARD_REQUEST_HEADERS 和 STANDARD_REPLY_HEADERS),以映射所有用户定义的头部。
与其使用通配符 (*),我们建议使用以下组合:STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS。
这样做可避免将 request 请求头映射到响应中。 |
从版本 4.3 开始,您可以通过在模式前添加 ! 来否定标头映射中的模式。
被否定的模式具有优先级,因此像 STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1 这样的列表不会映射 thing1、thing2 或 thing3。
它会映射标准标头 thing4 和 qux。
(注意:thing1 同时包含在非否定和否定形式中。
由于否定值具有优先级,thing1 不会被映射。)
如果您有一个以 ! 开头的用户自定义头部,且您希望对其进行映射,您可以使用 \ 进行转义,如下所示: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>
如果 mapped-request-headers 是 auth, ca*,则映射 auth、cat 和 can 头,但不映射 qux。
下面的示例展示了如何从名为 auth 的头部中获取名为 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());
...
从 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");
前例的结果是以下 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>
MTOM 支持
编组入站和出站 Web 服务网关通过 marshaller 的内置功能直接支持附件(例如,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));