Web Services 支援

本章描述了 Spring Integration 對 Web Services 的支援,包括

您需要在專案中包含此依賴項

  • Maven

  • Gradle

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

出站 Web Service 閘道器

當您將訊息傳送到通道時,可以透過兩種方式呼叫 Web Service,這兩種方式都基於 Spring Web Services 專案:SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway。前者接受 Stringjavax.xml.transform.Source 作為訊息 Payload。後者支援 MarshallerUnmarshaller 介面的任何實現。兩者都需要 Spring Web Services 的 DestinationProvider 來確定要呼叫的 Web Service 的 URI。以下示例展示了呼叫 Web Service 的這兩種方式

 simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);

 marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
當使用名稱空間支援(稍後描述)時,您只需設定一個 URI。在內部,解析器會配置一個固定的 URI DestinationProvider 實現。但是,如果您需要在執行時動態解析 URI,則 DestinationProvider 可以提供此類行為,例如從登錄檔中查詢 URI。有關此策略的更多資訊,請參閱 Spring Web Services DestinationProvider 的 Javadoc。

從 5.0 版本開始,您可以為 SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway 提供外部的 WebServiceTemplate 例項,您可以為該例項配置任何自定義屬性,包括 checkConnectionForFault(允許您的應用程式處理不規範的服務)。

有關內部工作的更多詳細資訊,請參閱 Spring Web Services 參考指南中關於客戶端訪問的章節以及關於物件/XML 對映的章節。

入站 Web Service 閘道器

當收到 Web Service 呼叫時將訊息傳送到通道,您同樣有兩種選擇:SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway。前者從 WebServiceMessage 中提取 javax.xml.transform.Source 並將其設定為訊息 Payload。後者支援 MarshallerUnmarshaller 介面的實現。如果傳入的 Web Service 訊息是 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 進行配置。

有關如何使用這些元件的更多詳細資訊,請參閱 Spring Web Services 參考指南中關於建立 Web Service 的章節。關於物件/XML 對映的章節也同樣適用。

要將 SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway 的配置新增到 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 Service 名稱空間支援

要配置出站 Web Service 閘道器,請使用 ws 名稱空間中的 outbound-gateway 元素,如下例所示

<int-ws:outbound-gateway id="simpleGateway"
                     request-channel="inputChannel"
                     uri="https://example.org"/>
此示例未提供 'reply-channel'。如果 Web Service 返回非空響應,包含該響應的 Message 將傳送到請求訊息的 REPLY_CHANNEL 頭部中定義的回覆通道。如果該通道不可用,則會丟擲通道解析異常。如果您想將回復發送到另一個通道,請在 'outbound-gateway' 元素上提供 'reply-channel' 屬性。
預設情況下,當您使用 String payload 傳送請求 Message 並呼叫返回空響應的 Web Service 時,不會發送回復 Message。因此,您無需設定 'reply-channel' 或在請求 Message 中包含 REPLY_CHANNEL 頭部。如果您確實希望接收空響應作為 Message,可以將 'ignore-empty-responses' 屬性設定為 false。但這僅對 String 物件有效,因為使用 SourceDocument 物件會導致空響應,從而永遠不會生成回覆 Message

要設定入站 Web Service 閘道器,請使用 inbound-gateway 元素,如下例所示

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

要使用 Spring OXM marshaller 或 unmarshaller,您必須提供 bean 引用。以下示例展示瞭如何為出站 marshalling 閘道器提供 bean 引用

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

以下示例展示瞭如何為入站 marshalling 閘道器提供 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)。

對於任何一種出站閘道器型別,還可以使用對任何 Spring Web Services 的 WebServiceMessageFactory 實現的引用來配置 message-factory 屬性。

對於簡單的入站閘道器型別,您可以將 extract-payload 屬性設定為 false,以便將整個 WebServiceMessage 而不僅僅是其 Payload 作為 Message 轉發到請求通道。例如,當自定義轉換器直接處理 WebServiceMessage 時,這樣做可能會很有用。

從 5.0 版本開始,web-service-template 引用屬性允許您注入一個具有任何可能自定義屬性的 WebServiceTemplate

Web Service Java DSL 支援

Web Service 名稱空間支援 中展示的閘道器的等效配置如下所示

@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}&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,則不支援變數替換,並且如果您提供了變數,則會發生配置錯誤。

控制 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 Service 閘道器會自動對映 SOAP action 頭部。預設情況下,它使用 DefaultSoapHeaderMapper 複製到 Spring Integration MessageHeaders 並從中複製。

您可以傳入自己實現的 SOAP 特定頭部對映器,因為閘道器具有支援此功能的屬性。

除非在 DefaultSoapHeaderMapperrequestHeaderNamesreplyHeaderNames 屬性中明確指定,否則任何使用者定義的 SOAP 頭部都不會被複制到 SOAP Message 或從 SOAP Message 中複製。

當您使用 XML 名稱空間進行配置時,可以使用 mapped-request-headersmapped-reply-headers 屬性來設定這些屬性,您也可以透過設定 header-mapper 屬性來提供自定義對映器。

對映使用者定義的頭部時,值也可以包含簡單的萬用字元模式(例如 myheader**myheader*)。例如,如果您需要複製所有使用者定義的頭部,可以使用萬用字元字元:*

從 4.1 版本開始,AbstractHeaderMapperDefaultSoapHeaderMapper 的父類)允許為 requestHeaderNamesreplyHeaderNames 屬性配置 NON_STANDARD_HEADERS 令牌(除了現有的 STANDARD_REQUEST_HEADERSSTANDARD_REPLY_HEADERS),以對映所有使用者定義的頭部。

建議使用以下組合代替萬用字元(*):STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS。這樣做可以避免將 request 頭部對映到 reply。

從 4.3 版本開始,您可以透過在模式前加上 ! 來否定頭部對映中的模式。否定模式具有優先權,因此像 STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1 這樣的列表不會對映 thing1thing2thing3。它會對映標準頭部、thing4qux。(請注意,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-headersauth, ca*,則 authcatcan 頭部會被對映,但 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 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 支援

marshalling 入站和出站 Web Service 閘道器透過 marshaller 的內建功能(例如,Jaxb2Marshaller 提供了 mtomEnabled 選項)直接支援附件。從 5.0 版本開始,簡單 Web Service 閘道器可以直接處理入站和出站的 MimeMessage 例項,這些例項具有用於操作附件的 API。當您需要傳送帶有附件的 Web Service 訊息(無論是來自伺服器的回覆還是客戶端請求)時,您應該直接使用 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));