Web 服务支持
本章介绍 Spring 集成对 Web 服务的支持,包括
您需要将此依赖项包含到您的项目中
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ws</artifactId>
<version>6.3.0</version>
</dependency>
compile "org.springframework.integration:spring-integration-ws:6.3.0"
出站 Web 服务网关
当您向通道发送消息时,要调用 Web 服务,您有两个选择,这两个选择都基于 Spring Web 服务 项目:SimpleWebServiceOutboundGateway
和 MarshallingWebServiceOutboundGateway
。前者接受 String
或 javax.xml.transform.Source
作为消息有效负载。后者支持 Marshaller
和 Unmarshaller
接口的任何实现。两者都需要一个 Spring Web 服务 DestinationProvider
,以确定要调用的 Web 服务的 URI。以下示例显示了调用 Web 服务的两种选择
simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);
marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
当使用命名空间支持(稍后描述)时,您只需要设置一个 URI。在内部,解析器配置了一个固定 URI DestinationProvider 实现。但是,如果您需要在运行时动态解析 URI,那么 DestinationProvider 可以提供此类行为,例如从注册表中查找 URI。有关此策略的更多信息,请参阅 Spring Web 服务 DestinationProvider Javadoc。
|
从 5.0 版本开始,您可以为 SimpleWebServiceOutboundGateway
和 MarshallingWebServiceOutboundGateway
提供一个外部 WebServiceTemplate
实例,您可以为其配置任何自定义属性,包括 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 服务 MessageEndpoint
接口,因此它们可以使用 MessageDispatcherServlet
进行配置,就像标准 Spring Web 服务配置一样。
要将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”属性。
|
默认情况下,当您调用一个 Web 服务,该服务在使用字符串有效负载作为请求Message 后返回空响应时,不会发送任何回复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
中,并从 MessageHeaders
中复制出来。
您可以传入您自己的 SOAP 特定头映射器实现,因为网关具有支持此操作的属性。
除非 DefaultSoapHeaderMapper
的 requestHeaderNames
或 replyHeaderNames
属性明确指定,否则任何用户定义的 SOAP 头都不会被复制到 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));