# Web 服务支持
# Web 服务支持
本章描述 Spring 集成对 Web 服务的支持,包括:
你需要在项目中包含此依赖项:
Maven
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ws</artifactId>
<version>5.5.9</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-ws:5.5.9"
# 出站 Web 服务网关
要在向通道发送消息时调用 Web 服务,你有两个选项,这两个选项都建立在Spring Web Services (opens new window)项目上: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 ](https://DOCS. Spring.io/ Spring-ws/DOCS/current/api/org/springframework/ws/client/support/destinationprovider.html)Javadoc 以获取有关此策略的更多信息。 |
---|
从版本 5.0 开始,你可以提供带有外部SimpleWebServiceOutboundGateway
和MarshallingWebServiceOutboundGateway
实例的WebServiceTemplate
实例,你可以为任何自定义属性配置该实例,包括checkConnectionForFault
(它允许你的应用程序处理不符合要求的服务)。
有关内部工作的更多详细信息,请参见 Spring Web 服务参考指南中涵盖客户端访问 (opens new window)的章节和涵盖对象/XML 映射 (opens new window)的章节。
# 入站 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
接口,因此可以根据标准 Spring Web 服务配置使用MessageDispatcherServlet
进行配置。
有关如何使用这些组件的更多详细信息,请参见 Spring Web 服务参考指南中涵盖创建 Web 服务 (opens new window)的章节。涵盖对象/XML 映射 (opens new window)的章节也同样适用。
要将SimpleWebServiceInboundGateway
和MarshallingWebServiceInboundGateway
配置添加到 Spring WS 基础设施,你应该在MessageDispatcherServlet
和目标MessageEndpoint
实现之间添加EndpointMapping
定义,就像在普通 Spring WS 应用程序中那样。为此(从 Spring 集成角度), 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 定义。
有关更多信息,请参见端点映射 (opens new window)。
# Web 服务命名空间支持
要配置出站 Web 服务网关,请使用ws
命名空间中的outbound-gateway
元素,如下例所示:
<int-ws:outbound-gateway id="simpleGateway"
request-channel="inputChannel"
uri="https://example.org"/>
此示例不提供“回复通道”。 如果 Web 服务返回一个非空响应,则包含该响应的 Message 将被发送到请求消息的REPLY_CHANNEL 头中定义的回复通道。如果不可用,将抛出一个通道解析异常。 如果你想将答复发送到另一个通道,请在“出站网关”元素上提供一个“reply-channel”属性。 |
---|
默认情况下,当你调用一个 Web 服务,在为请求Message 使用字符串有效负载后返回一个空响应时,不会发送任何回复Message 。因此,,你不需要在请求 Message 中设置“reply-channel”或有REPLY_CHANNEL 头。如果你确实希望以 Message 的形式接收空响应,那么你可以将“ignore-empty-responses”属性设置为false 。只对 String 对象有效,因为使用Source 或Document 对象会导致 null 响应,因此永远不会生成答复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
转发到请求通道,而不仅仅是将其有效负载作为Message
转发到请求通道。这样做可能是有用的,例如,当一个自定义转换器直接针对WebServiceMessage
工作时。
从版本 5.0 开始,web-service-template
reference 属性允许你使用任何可能的自定义属性注入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 服务支持的所有 URI 方案(参见URI 和传输 (opens new window)),提供了<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
](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/web/util/uricomponentsbuilder.html))到 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 集成 Web 服务网关自动映射 SOAP 动作报头。默认情况下,通过使用[DefaultSoapHeaderMapper
](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/ws/defaultsoapheadermapper.html)来复制 Spring IntegrationMessageHeaders
。
你可以传入你自己的特定于 SOAP 的头映射器的实现,因为网关具有支持这样做的属性。
除非DefaultSoapHeaderMapper
的requestHeaderNames
或replyHeaderNames
属性明确指定,否则任何用户定义的 SOAP 头都不会复制到或复制到 SOAP 消息。
当使用 XML 名称空间进行配置时,可以通过使用mapped-request-headers
和mapped-reply-headers
属性来设置这些属性,也可以通过设置header-mapper
属性来提供自定义映射器。
在映射用户定义的标头时,值还可以包含简单的通配符模式(例如myheader* 或**myheader** )。例如,如果需要复制所有用户定义的标头,可以使用通配符:``。 |
---|
从版本 4.1 开始,AbstractHeaderMapper
(aDefaultSoapHeaderMapper
超类)允许将NON_STANDARD_HEADERS
令牌配置为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 服务网关通过编组器的内置功能直接支持附件(例如,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));