Web Services Support

本章介绍 Spring Integration 对 Web 服务的支持,包括:

This chapter describes Spring Integration’s support for web services, including:

你需要将此依赖项包含在你的项目中:

You need to include this dependency into your project:

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-ws</artifactId>
    <version>{project-version}</version>
</dependency>
compile "org.springframework.integration:spring-integration-ws:{project-version}"

Outbound Web Service Gateways

要在向通道发送消息时调用 Web 服务,你有两个选择,这两个选择都建立在 Spring Web Services 项目上:SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway。前者接受 Stringjavax.xml.transform.Source 作为消息有效负载。后者支持任何 MarshallerUnmarshaller 接口的实现。两者都需要一个 Spring Web Services DestinationProvider,以确定要调用的 Web 服务的 URI。以下示例展示了调用 Web 服务的两种选择:

To invoke a web service when you send a message to a channel, you have two options, both of which build upon the Spring Web Services project: SimpleWebServiceOutboundGateway and MarshallingWebServiceOutboundGateway. The former accepts either a String or javax.xml.transform.Source as the message payload. The latter supports any implementation of the Marshaller and Unmarshaller interfaces. Both require a Spring Web Services DestinationProvider, to determine the URI of the web service to be called. The following example shows both options for invoking a web service:

 simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);

 marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);

当使用名称空间支持 (described later) 时,你只需要设置一个 URI。在内部,解析器会配置一个固定的 URI DestinationProvider 实现。但是,如果你需要在运行时动态解析 URI,则 DestinationProvider 可以提供此类行为,例如从注册表中查找 URI。有关此策略的详细信息,请参阅 Spring Web Services DestinationProvider Javadoc。

When using the namespace support (described later), you need only set a URI. Internally, the parser configures a fixed URI DestinationProvider implementation. If you need dynamic resolution of the URI at runtime, however, then the DestinationProvider can provide such behavior as looking up the URI from a registry. See the Spring Web Services DestinationProvider Javadoc for more information about this strategy.

从版本 5.0 开始,您可以为 SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway 提供一个外部 WebServiceTemplate 实例,您可以为它配置任何自定义属性,包括 checkConnectionForFault(它允许您的应用程序处理不一致的服务)。

Starting with version 5.0, you can supply the SimpleWebServiceOutboundGateway and MarshallingWebServiceOutboundGateway with an external WebServiceTemplate instance, which you can configure for any custom properties, including checkConnectionForFault (which allows your application to deal with non-conforming services).

有关内部工作原理的更多详细信息,请参阅 Spring Web Services 参考指南中介绍 client access 的章节和介绍 Object/XML mapping 的章节。

For more detail on the inner workings, see the Spring Web Services reference guide’s chapter covering client access and the chapter covering Object/XML mapping.

Inbound Web Service Gateways

在接收 Web 服务调用时将消息发送到通道,您还有两个选项:SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway。前者从 WebServiceMessage 中提取 javax.xml.transform.Source 并将其设置为消息有效负载。后者支持 MarshallerUnmarshaller 接口的实现。如果传入的 Web 服务消息是 SOAP 消息,则将 SOAP 操作头添加到转发到请求通道的 Message 头中。以下示例显示了两个选项:

To send a message to a channel upon receiving a web service invocation, you again have two options: SimpleWebServiceInboundGateway and MarshallingWebServiceInboundGateway. The former extracts a javax.xml.transform.Source from the WebServiceMessage and sets it as the message payload. The latter supports implementation of the Marshaller and Unmarshaller interfaces. If the incoming web service message is a SOAP message, the SOAP action header is added to the headers of the Message that is forwarded onto the request channel. The following example shows both options:

 simpleGateway = new SimpleWebServiceInboundGateway();
 simpleGateway.setRequestChannel(forwardOntoThisChannel);
 simpleGateway.setReplyChannel(listenForResponseHere); //Optional

 marshallingGateway = new MarshallingWebServiceInboundGateway(marshaller);
 //set request and optionally reply channel

两个网关都实现了 Spring Web Services MessageEndpoint 接口,因此它们可以使用 MessageDispatcherServlet 按照标准 Spring Web Services 配置进行配置。

Both gateways implement the Spring Web Services MessageEndpoint interface, so they can be configured with a MessageDispatcherServlet as per standard Spring Web Services configuration.

有关如何使用这些组件的更多详细信息,请参阅 Spring Web Services 参考指南中介绍 creating a web service 的章节。介绍 Object/XML mapping 的章节也同样适用。

For more detail on how to use these components, see the Spring Web Services reference guide’s chapter covering creating a web service. The chapter covering Object/XML mapping is also applicable again.

要将 SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway 配置添加到 Spring WS 基础设施,您应在 MessageDispatcherServlet 和目标 MessageEndpoint 实现之间添加 EndpointMapping 定义,就像对于普通的 Spring WS 应用程序一样。出于此目的(从 Spring 集成角度来看),Spring WS 提供了以下方便的 EndpointMapping 实现:

To add the SimpleWebServiceInboundGateway and MarshallingWebServiceInboundGateway configurations to the Spring WS infrastructure, you should add the EndpointMapping definition between MessageDispatcherServlet and the target MessageEndpoint implementations, as you would for a normal Spring WS application. For this purpose (from the Spring Integration perspective), Spring WS provides the following convenient EndpointMapping implementations:

  • 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 定义。

You must specify the beans for these classes in the application context and reference the SimpleWebServiceInboundGateway and/or MarshallingWebServiceInboundGateway bean definitions according to the WS mapping algorithm.

有关详细信息,请参阅 endpoint mappings

See the endpoint mappings for more information.

Web Service Namespace Support

要配置出站 Web 服务网关,请使用 ws 命名空间中的 outbound-gateway 元素,如下例所示:

To configure an outbound web service gateway, use the outbound-gateway element from the ws namespace, as the following example shows:

<int-ws:outbound-gateway id="simpleGateway"
                     request-channel="inputChannel"
                     uri="https://example.org"/>

此示例不提供“回复频道”。如果 Web 服务返回一个非空响应,则包含该响应的 Message 会被发送到请求消息的 REPLY_CHANNEL 头中定义的回复频道。如果不可用,则会引发频道解决异常。如果想要将回复发送到另一个频道,请在“outbound-gateway”元素中提供“reply-channel”属性。

This example does not provide a 'reply-channel'. If the web service returns a non-empty response, the Message containing that response is sent to the reply channel defined in the request message’s REPLY_CHANNEL header. If that is not available, a channel resolution exception is thrown. If you want to send the reply to another channel instead, provide a 'reply-channel' attribute on the 'outbound-gateway' element.

默认情况下,当你调用一个在使用字符串有效负载时返回空响应的网络服务,Message 中就没有 Message 答复发送。因此,你不需要设置“答复渠道”或在请求 Message 中具有 REPLY_CHANNEL 头部。如果你实际想要接收空响应作为 Message,那么你可以将“忽略空响应”属性设置为 false。这样做仅适用于 String 对象,因为使用 SourceDocument 对象将导致空答复并因此永远不会生成 Message 答复。

By default, when you invoke a web service that returns an empty response after using a String payload for the request Message, no reply Message is sent. Therefore, you need not set a 'reply-channel' or have a REPLY_CHANNEL header in the request Message. If you actually do want to receive the empty response as a Message, you can set the 'ignore-empty-responses' attribute to false. Doing so works only for String objects, because using a Source or a Document object leads to a null response and consequently never generates a reply Message.

要设置入站 Web 服务网关,请使用 inbound-gateway 元素,如下例所示:

To set up an inbound Web Service Gateway, use the inbound-gateway element, as the following example shows:

<int-ws:inbound-gateway id="simpleGateway"
                    request-channel="inputChannel"/>

要使用 Spring OXM 转换器或非转换器,您必须提供 bean 引用。以下示例显示如何为出站转换网关提供 bean 引用:

To use Spring OXM marshallers or unmarshallers, you must provide bean references. The following example shows how to provide a bean reference for an outbound marshalling gateway:

<int-ws:outbound-gateway id="marshallingGateway"
                     request-channel="requestChannel"
                     uri="https://example.org"
                     marshaller="someMarshaller"
                     unmarshaller="someUnmarshaller"/>

以下示例显示了如何为入站转换网关提供 bean 引用:

The following example shows how to provide a bean reference for an inbound marshalling gateway:

<int-ws:inbound-gateway id="marshallingGateway"
                    request-channel="requestChannel"
                    marshaller="someMarshaller"
                    unmarshaller="someUnmarshaller"/>

大多数 Marshaller 实现也实现 Unmarshaller 接口。当使用这样的 Marshaller 时,只有 marshaller 属性是必须的。即使是使用 Marshaller,你也可以为出站网关上的 request-callback 提供引用。

Most Marshaller implementations also implement the Unmarshaller interface. When using such a Marshaller, only the marshaller attribute is necessary. Even when using a Marshaller, you may also provide a reference for the request-callback on the outbound gateways.

对于任一类型的出站网关,你可以指定 destination-provider 属性,而不是 uri(其中确需一项)。随后你可以引用任何 Spring Web Services DestinationProvider 实现(例如,在运行时从注册表中查找 URI)。

For either outbound gateway type, you can specify a destination-provider attribute instead of the uri (exactly one of them is required). You can then reference any Spring Web Services DestinationProvider implementation (for example, to lookup the URI from a registry at runtime).

对于任一类型的出站网关,还可以使用对任何 Spring Web Services WebServiceMessageFactory 实现的引用来配置 message-factory 属性。

For either outbound gateway type, the message-factory attribute can also be configured with a reference to any Spring Web Services WebServiceMessageFactory implementation.

对于简单的入站网关类型,你可以将 extract-payload 属性设置为 false,以便将整个 WebServiceMessage 转发,而不是将其有效负载作为 Message 转发到请求通道。这样做可能很有用,例如,当定制转换器直接适用于 WebServiceMessage 时。

For the simple inbound gateway type, you can set the extract-payload attribute to false to forward the entire WebServiceMessage instead of just its payload as a Message to the request channel. Doing so might be useful, for example, when a custom transformer works against the WebServiceMessage directly.

从版本 5.0 开始,web-service-template 引用属性允许你注入具有任何可能的定制属性的 WebServiceTemplate

Starting with version 5.0, the web-service-template reference attribute lets you inject a WebServiceTemplate with any possible custom properties.

Web Service Java DSL Support

Web Service Namespace Support 中展示的网关的等效配置在以下代码段中展示:

The equivalent configuration for the gateways shown in Web Service Namespace Support are shown in the following snippets:

@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)。示例:

Other properties can be set on the endpoint specs in a fluent manner (with the properties depending on whether an external WebServiceTemplate has been provided for outbound gateways). Examples:

.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))
)

Outbound URI Configuration

对于 Spring Web Services 支持的所有 URI 方案(参见 URIs and Transports),均提供了 <uri-variable/> 替换。以下示例展示如何定义它:

For all URI schemes supported by Spring Web Services (see URIs and Transports) <uri-variable/> substitution is provided. The following example shows how to define it:

<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}&amp;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,则不支持变量替换,并且如果你提供变量,就会发生配置错误。

If you supply a DestinationProvider, variable substitution is not supported and a configuration error occurs if you provide variables.

Controlling URI Encoding

在将请求发送出去之前,URL 字符串默认编码为(参见 UriComponentsBuilder)URI 对象。在一些具有非标准 URI 的场景中,最好不要执行编码。 <ws:outbound-gateway/> 元素提供了一个 encoding-mode 属性。要禁用对 URL 的编码,请将此属性设置为 NONE(默认情况下,它为 TEMPLATE_AND_VALUES)。如果你希望对某些 URL 部分进行编码,你可以通过在 <uri-variable/> 中使用 expression 来执行此操作,如下面的示例所示:

By default, the URL string is encoded (see UriComponentsBuilder) to the URI object before sending the request. In some scenarios with a non-standard URI, it is undesirable to perform the encoding. The <ws:outbound-gateway/> element provides an encoding-mode attribute. To disable encoding the URL, set this attribute to NONE (by default, it is TEMPLATE_AND_VALUES). If you wish to partially encode some of the URL, you can do so by using an expression within a <uri-variable/>, as the following example shows:

<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 将被忽略。

If you set DestinationProvider, encoding-mode is ignored.

WS Message Headers

Spring Integration Web 服务网关自动映射 SOAP 操作标头。在默认情况下,它会使用 DefaultSoapHeaderMapper 来将标头从 Spring Integration MessageHeaders 复制到 Spring Integration MessageHeaders,反之亦然。

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 特定头映射程序的实现,因为网关具有支持此操作的属性。

You can pass in your own implementation of SOAP-specific header mappers, as the gateways have properties to support doing so.

除非显式指定 DefaultSoapHeaderMapperrequestHeaderNamesreplyHeaderNames 属性,否则任何用户定义的 SOAP 头都不会复制到 SOAP 消息中或从 SOAP 消息中复制。

Unless explicitly specified by the requestHeaderNames or replyHeaderNames properties of the DefaultSoapHeaderMapper, any user-defined SOAP headers are not copied to or from a SOAP Message.

当你使用 XML 命名空间进行配置时,可以使用 mapped-request-headersmapped-reply-headers 属性设置这些属性,你可以通过设置 header-mapper 属性提供一个定制的映射器。

When you use the XML namespace for configuration, you can set these properties by using the mapped-request-headers and mapped-reply-headers attributes, you can provide a custom mapper by setting the header-mapper attribute.

当映射用户自定义头部时,值也可以包含简单的通配符号模式(例如 myheader*myheader). For example, if you need to copy all user-defined headers, you can use the wildcard character: .

When mapping user-defined headers, the values can also contain simple wildcard patterns (such myheader* or myheader). For example, if you need to copy all user-defined headers, you can use the wildcard character: .

从版本 4.1 开始,AbstractHeaderMapper(一个 DefaultSoapHeaderMapper 超类)允许在 requestHeaderNamesreplyHeaderNames 属性中配置 NON_STANDARD_HEADERS 令牌(除了现有的 STANDARD_REQUEST_HEADERSSTANDARD_REPLY_HEADERS)以映射所有用户定义的头。

Starting with version 4.1, the AbstractHeaderMapper (a DefaultSoapHeaderMapper superclass) lets the NON_STANDARD_HEADERS token be configured for the requestHeaderNames and replyHeaderNames properties (in addition to existing STANDARD_REQUEST_HEADERS and STANDARD_REPLY_HEADERS) to map all user-defined headers.

与其使用通配符号 (*),我们推荐使用以下组合:STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS。这样做可避免将 request 头部映射到答复。

Rather than using the wildcard (*), we recommend using the following combination : STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS. Doing so avoids mapping request headers to the reply.

从版本 4.3 开始,你可以通过在模式前加上 ! 来取消头映射中的模式。取消的模式具有优先级,因此诸如 STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1 的列表不会映射 thing1thing2thing3。它确实映射标准头、thing4qux。(注意 thing1 同时包含在未取消和已取消的形式中。由于已取消的值优先,因此 thing1 未被映射。)

Starting with version 4.3, you can negate patterns in the header mappings by preceding the pattern with !. Negated patterns get priority, so a list such as STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1 does not map thing1, thing2, or thing3. It does map the standard headers, thing4, and qux. (Note that thing1 is included in both non-negated and negated forms. Because negated values take precedence, thing1 is not mapped.)

如果你有一个以 ! 开头的你要映射的用户自定义头部,那么你可以使用 \ 对其进行转义,如下所示:STANDARD_REQUEST_HEADERS,\!myBangHeader。然后映射到 !myBangHeader

If you have a user-defined header that begins with ! that you do wish to map, you can escape it with \, as follows: STANDARD_REQUEST_HEADERS,\!myBangHeader. A !myBangHeader is then mapped.

入站 SOAP 头(入站网关的请求头和出站网关的应答头)映射为 SoapHeaderElement 对象。你可以通过访问 Source 来浏览内容:

Inbound SOAP headers (request headers for the inbound gateway and reply headers for the outbound gateway) are mapped as SoapHeaderElement objects. You can explore the contents by accessing the 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-headersauth, ca*,则映射 authcatcan 头,但不会映射 qux

If mapped-request-headers is auth, ca*, the auth, cat, and can headers are mapped, but qux is not mapped.

以下示例展示了如何从名为 auth 的头中获取名为 user 的值:

The following example shows how to get a value named user from a header named 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> 的子节点。以下示例展示了如何执行此操作:

Starting with version 5.0, the DefaultSoapHeaderMapper supports user-defined headers of type javax.xml.transform.Source and populates them as child nodes of the <soapenv:Header>. The following example shows how to do so:

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 规范:

The result of the preceding examples is the following SOAP envelope:

<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 Support

规范边界传入和传出 Web 服务网关直接通过规范器的内置功能支持附件(例如,Jaxb2Marshaller 提供 mtomEnabled 选项)。从版本 5.0 开始,简单的 Web 服务网关可以直接操作传入和传出 MimeMessage 实例,该实例有一个 API 用于操作附件。当您需要发送带附件的 Web 服务消息(无论是服务器回复还是客户端请求)时,您都应直接使用 WebServiceMessageFactory 并发送带有附件的 WebServiceMessage 作为网关请求或回复通道中的`payload`。以下示例演示如何执行此操作:

The marshalling inbound and outbound web service gateways support attachments directly through built-in functionality of the marshaller (for example, Jaxb2Marshaller provides the mtomEnabled option). Starting with version 5.0, the simple web service gateways can directly operate with inbound and outbound MimeMessage instances, which have an API to manipulate attachments. When you need to send web service message with attachments (either a reply from a server or a client request) you should use the WebServiceMessageFactory directly and send a WebServiceMessage with attachments as a payload to the request or reply channel of the gateway. The following example shows how to do so:

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));