Web 服务支持
本章描述了 Spring Integration 对 Web 服务的支持,包括
您需要将此依赖项包含到您的项目中
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ws</artifactId>
<version>6.3.5</version>
</dependency>
compile "org.springframework.integration:spring-integration-ws:6.3.5"
出站 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 版本开始,您可以为 SimpleWebServiceOutboundGateway
和 MarshallingWebServiceOutboundGateway
提供一个外部 WebServiceTemplate
实例,您可以为其配置任何自定义属性,包括 checkConnectionForFault
(允许您的应用程序处理不符合规范的服务)。
入站 Web 服务网关
要在接收 Web 服务调用后向通道发送消息,您还有两个选项:SimpleWebServiceInboundGateway
和 MarshallingWebServiceInboundGateway
。前者从 WebServiceMessage
中提取 javax.xml.transform.Source
并将其设置为消息有效负载。后者支持Marshaller
和 Unmarshaller
接口的实现。如果传入的 Web 服务消息是 SOAP 消息,则 SOAP action 头将添加到转发到请求通道的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 基础结构中,您应该在 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 服务命名空间支持
要配置出站 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 的 String 有效负载调用返回空响应的 Web 服务时,不会发送回复Message 。因此,您无需设置“reply-channel”或在请求Message 中包含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 服务 Java DSL 支持
在以下代码段中显示了Web 服务命名空间支持中显示的网关的等效配置
@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 字符串会被编码(请参阅 UriComponentsBuilder
)到 URI 对象,然后发送请求。在某些具有非标准 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 消息头
Spring Integration Web 服务网关会自动映射 SOAP action 头。默认情况下,它使用 DefaultSoapHeaderMapper
复制到和从 Spring Integration MessageHeaders
。
您可以传入您自己的 SOAP 特定头映射器的实现,因为网关具有支持这样做的属性。
除非DefaultSoapHeaderMapper
的requestHeaderNames
或replyHeaderNames
属性明确指定,否则任何用户定义的 SOAP 头都不会复制到或从 SOAP 消息中复制。
当您使用 XML 命名空间进行配置时,您可以使用mapped-request-headers
和mapped-reply-headers
属性设置这些属性,您可以通过设置header-mapper
属性来提供自定义映射器。
映射用户定义的头时,值也可以包含简单的通配符模式(例如myheader* 或**myheader** )。例如,如果您需要复制所有用户定义的头,您可以使用通配符字符:* 。 |
从 4.1 版本开始,AbstractHeaderMapper
(DefaultSoapHeaderMapper
的超类)允许为requestHeaderNames
和replyHeaderNames
属性配置NON_STANDARD_HEADERS
令牌(除了现有的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服务网关直接通过编组程序的内置功能支持附件(例如,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));