网络服务支持
本章介绍 Spring Integration 对 Web 服务的支持,包括:
您需要将此依赖项包含到您的项目中:
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ws</artifactId>
<version>5.5.13</version>
</dependency>
compile "org.springframework.integration:spring-integration-ws:5.5.13"
出站 Web 服务网关
要在向通道发送消息时调用 Web 服务,您有两个选项,它们都基于Spring Web 服务项目:SimpleWebServiceOutboundGateway
和MarshallingWebServiceOutboundGateway
. 前者接受 aString
或javax.xml.transform.Source
作为消息有效负载。后者支持Marshaller
和Unmarshaller
接口的任何实现。两者都需要 Spring Web ServicesDestinationProvider
来确定要调用的 Web 服务的 URI。以下示例显示了调用 Web 服务的两个选项:
simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);
marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
当使用命名空间支持(稍后描述)时,您只需要设置一个 URI。在内部,解析器配置一个固定的 URIDestinationProvider 实现。但是,如果您需要在运行时动态解析 URI,则DestinationProvider 可以提供诸如从注册表中查找 URI 之类的行为。有关此策略的更多信息,请参阅 Spring Web Services DestinationProvider Javadoc。
|
从 5.0 版开始,您可以为SimpleWebServiceOutboundGateway
andMarshallingWebServiceOutboundGateway
提供一个外部WebServiceTemplate
实例,您可以为任何自定义属性配置该实例,包括checkConnectionForFault
(允许您的应用程序处理不符合要求的服务)。
入站 Web 服务网关
要在接收到 Web 服务调用时向通道发送消息,您还有两个选择:SimpleWebServiceInboundGateway
和MarshallingWebServiceInboundGateway
. 前者javax.xml.transform.Source
从 中提取 aWebServiceMessage
并将其设置为消息有效负载。后者支持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 ServicesMessageEndpoint
接口,因此可以MessageDispatcherServlet
按照标准 Spring Web Services 配置对其进行配置。
要将SimpleWebServiceInboundGateway
和MarshallingWebServiceInboundGateway
配置添加到 Spring WS 基础架构,您应该在和 目标实现EndpointMapping
之间添加定义,就像您为普通 Spring WS 应用程序所做的那样。为此(从 Spring 集成的角度来看),Spring WS 提供了以下方便的实现:MessageDispatcherServlet
MessageEndpoint
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
和/或bean 定义。MarshallingWebServiceInboundGateway
有关更多信息,请参阅端点映射。
Web 服务命名空间支持
要配置出站 Web 服务网关,请使用命名空间中的outbound-gateway
元素ws
,如以下示例所示:
<int-ws:outbound-gateway id="simpleGateway"
request-channel="inputChannel"
uri="https://example.org"/>
此示例不提供“回复通道”。如果 Web 服务返回非空响应,则Message 包含该响应的内容将发送到请求消息REPLY_CHANNEL 标头中定义的回复通道。如果不可用,则会引发通道解析异常。如果您想将回复发送到另一个通道,请在“出站网关”元素上提供“回复通道”属性。
|
默认情况下,当您调用的 Web 服务在对请求使用字符串有效负载后返回空响应时Message ,不会发送任何回复Message 。因此,您无需设置“回复通道”或REPLY_CHANNEL 在请求中包含标头Message 。如果您确实想以 接收空响应Message ,则可以将 'ignore-empty-responses' 属性设置为false 。这样做仅适用于String 对象,因为使用 aSource 或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 服务DestinationProvider
实现(例如,在运行时从注册表中查找 URI)。
对于任一出站网关类型,message-factory
还可以使用对任何 Spring Web 服务WebServiceMessageFactory
实现的引用来配置属性。
对于简单入站网关类型,您可以将extract-payload
属性设置false
为将整个WebServiceMessage
而不是仅将其有效负载作为 a转发Message
到请求通道。这样做可能很有用,例如,当自定义转换器WebServiceMessage
直接与 .
从版本 5.0 开始,web-service-template
引用属性允许您注入WebServiceTemplate
任何可能的自定义属性。
Web 服务 Java DSL 支持
Web 服务命名空间支持中显示的网关的等效配置显示在以下片段中:
@Bean
IntegrationFlow inbound() {
return IntegrationFlows.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 IntegrationFlows.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 方案(请参阅URIs 和 Transports)<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 编码
默认情况下,UriComponentsBuilder
在发送请求之前,URL 字符串会被编码(参见 )为 URI 对象。在某些使用非标准 URI 的情况下,不希望执行编码。该<ws:outbound-gateway/>
元素提供了一个encoding-mode
属性。要禁用 URL 编码,请将此属性设置为NONE
(默认为TEMPLATE_AND_VALUES
)。如果您希望对某些 URL 进行部分编码,可以通过使用expression
inside a来实现<uri-variable/>
,如以下示例所示:
<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 消息头
Spring Integration Web 服务网关自动映射 SOAP 操作标头。默认情况下,它MessageHeaders
使用DefaultSoapHeaderMapper
.
您可以传入您自己的特定于 SOAP 的标头映射器的实现,因为网关具有支持这样做的属性。
除非由 的requestHeaderNames
或replyHeaderNames
属性明确指定,否则DefaultSoapHeaderMapper
任何用户定义的 SOAP 标头都不会复制到 SOAP 消息或从 SOAP 消息复制。
当您使用 XML 命名空间进行配置时,您可以使用mapped-request-headers
和mapped-reply-headers
属性来设置这些属性,您可以通过设置属性来提供自定义映射器header-mapper
。
映射用户定义的标头时,值还可以包含简单的通配符模式(例如myheader* 或myheader )。例如,如果您需要复制所有用户定义的标头,则可以使用通配符: .
|
从版本 4.1 开始,AbstractHeaderMapper
(DefaultSoapHeaderMapper
超类)允许为and属性NON_STANDARD_HEADERS
配置令牌(除了现有的and )以映射所有用户定义的标头。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 然后映射
A。 |
入站 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
映射。
以下示例显示如何user
从名为 的标头中获取命名值auth
:
...
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 服务网关直接通过编组器的内置功能支持附件(例如,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));