HTTP 名稱空間支援

Spring Integration 提供了 http 名稱空間以及相應的 schema 定義。要將其包含在配置中,請在應用程式上下文配置檔案中提供以下名稱空間宣告:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-http="http://www.springframework.org/schema/integration/http"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/http
    https://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
    ...
</beans>

入站

XML 名稱空間提供了用於處理 HTTP 入站請求的兩個元件:inbound-channel-adapterinbound-gateway。要處理不需要返回專用響應的請求,請使用 inbound-channel-adapter。以下示例顯示瞭如何配置一個:

<int-http:inbound-channel-adapter id="httpChannelAdapter" channel="requests"
    supported-methods="PUT, DELETE"/>

要處理需要響應的請求,請使用 inbound-gateway。以下示例顯示瞭如何配置一個:

<int-http:inbound-gateway id="inboundGateway"
    request-channel="requests"
    reply-channel="responses"/>

請求對映支援

Spring Integration 3.0 透過引入 IntegrationRequestMappingHandlerMapping 改進了 REST 支援。該實現依賴於 Spring Framework 3.1 或更高版本提供的增強 REST 支援。

對 HTTP 入站閘道器或 HTTP 入站通道介面卡的解析會註冊一個 integrationRequestMappingHandlerMapping bean,其型別為 IntegrationRequestMappingHandlerMapping(如果尚未註冊)。HandlerMapping 的這個特定實現將其邏輯委託給 RequestMappingInfoHandlerMapping。該實現提供了類似於 Spring MVC 中 org.springframework.web.bind.annotation.RequestMapping 註解的功能。

有關更多資訊,請參見 使用 @RequestMapping 對映請求

為此,Spring Integration 3.0 引入了 <request-mapping> 元素。您可以將此可選元素新增到 <http:inbound-channel-adapter><http:inbound-gateway> 中。它與 pathsupported-methods 屬性結合使用。以下示例顯示瞭如何在入站閘道器上配置它:

<inbound-gateway id="inboundController"
    request-channel="requests"
    reply-channel="responses"
    path="/foo/{fooId}"
    supported-methods="GET"
    view-name="foo"
    error-code="oops">
   <request-mapping headers="User-Agent"
     params="myParam=myValue"
     consumes="application/json"
     produces="!text/plain"/>
</inbound-gateway>

根據前面的配置,名稱空間解析器會建立 IntegrationRequestMappingHandlerMapping(如果不存在)和 HttpRequestHandlingController bean 的例項,並將其與 RequestMapping 例項關聯。此 RequestMapping 例項隨後會轉換為 Spring MVC 的 RequestMappingInfo

<request-mapping> 元素提供以下屬性:

  • headers

  • params

  • consumes

  • produces

透過 <http:inbound-channel-adapter><http:inbound-gateway>pathsupported-methods 屬性,<request-mapping> 屬性直接轉換為 Spring MVC 中 org.springframework.web.bind.annotation.RequestMapping 註解提供的相應選項。

<request-mapping> 元素允許您將多個 Spring Integration HTTP 入站端點配置到同一個 path(甚至同一個 supported-methods),並允許您根據傳入的 HTTP 請求提供不同的下游訊息流。

或者,您也可以只宣告一個 HTTP 入站端點,並在 Spring Integration 流中應用路由和過濾邏輯以達到相同的結果。這允許您儘早將 Message 引入流中。以下示例顯示瞭如何實現:

<int-http:inbound-gateway request-channel="httpMethodRouter"
    supported-methods="GET,DELETE"
    path="/process/{entId}"
    payload-expression="#pathVariables.entId"/>

<int:router input-channel="httpMethodRouter" expression="headers.http_requestMethod">
    <int:mapping value="GET" channel="in1"/>
    <int:mapping value="DELETE" channel="in2"/>
</int:router>

<int:service-activator input-channel="in1" ref="service" method="getEntity"/>

<int:service-activator input-channel="in2" ref="service" method="delete"/>

有關處理程式對映的更多資訊,請參見 Spring Framework Web Servlet 文件Spring Framework Web Reactive 文件

IntegrationRequestMappingHandlerMapping 擴充套件了 Spring MVC 的 RequestMappingHandlerMapping 類,繼承了其大部分邏輯,特別是 handleNoMatch(Set, String, HttpServletRequest),當由於某種原因對映不匹配時,它會為 HTTP 響應丟擲特定的 4xx 錯誤,從而阻止呼叫應用程式上下文中任何剩餘的對映處理器。因此,Spring Integration 和 Spring MVC 請求對映配置相同的 path(例如,一箇中是 POST,另一箇中是 GET)是不受支援的;MVC 對映將找不到。

跨域資源共享 (CORS) 支援

從 4.2 版本開始,您可以使用 <cross-origin> 元素配置 <http:inbound-channel-adapter><http:inbound-gateway>。它代表與 Spring MVC @Controller 註解的 @CrossOrigin 相同的選項,並允許為 Spring Integration HTTP 端點配置跨域資源共享 (CORS):

  • origin:允許的來源列表。* 表示允許所有來源。這些值被放置在預檢響應和實際響應的 Access-Control-Allow-Origin 頭中。預設值為 *

  • allowed-headers:指示在實際請求期間可以使用哪些請求頭。* 表示允許客戶端請求的所有頭。此屬性控制預檢響應的 Access-Control-Allow-Headers 頭的值。預設值為 *

  • exposed-headers:使用者代理允許客戶端訪問的響應頭列表。此屬性控制實際響應的 Access-Control-Expose-Headers 頭的值。

  • method:允許的 HTTP 請求方法:GETPOSTHEADOPTIONSPUTPATCHDELETETRACE。此處指定的方法會覆蓋 supported-methods 中的方法。

  • allow-credentials:如果瀏覽器應包含與請求域關聯的任何 cookie,則設定為 true;否則設定為 false。空字串 ("") 表示未定義。如果為 true,則預檢響應包含 Access-Control-Allow-Credentials=true 頭。預設值為 true

  • max-age:控制預檢響應的快取持續時間。將其設定為合理的值可以減少瀏覽器所需的預檢請求-響應互動次數。此屬性控制預檢響應中 Access-Control-Max-Age 頭的值。-1 表示未定義。預設值為 1800 秒(30 分鐘)。

CORS Java 配置由 org.springframework.integration.http.inbound.CrossOrigin 類表示,其例項可以注入到 HttpRequestHandlingEndpointSupport bean 中。

響應狀態碼

從 4.1 版本開始,您可以使用 status-code-expression 配置 <http:inbound-channel-adapter> 來覆蓋預設的 200 OK 狀態。該表示式必須返回一個可轉換為 org.springframework.http.HttpStatus 列舉值的物件。evaluationContext 具有 BeanResolver,並且從 5.1 版本開始,將 RequestEntity<?> 作為根物件提供。例如,可以在執行時解析返回狀態碼值的某個作用域 bean。但是,更有可能的是將其設定為固定值,例如 status-code=expression="204" (No Content),或 status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT"。預設情況下,status-code-expression 為 null,這意味著返回正常的 '200 OK' 響應狀態。使用 RequestEntity<?> 作為根物件,狀態碼可以是有條件的,例如取決於請求方法、某些頭、URI 內容甚至請求體。以下示例顯示瞭如何將狀態碼設定為 ACCEPTED

<http:inbound-channel-adapter id="inboundController"
       channel="requests" view-name="foo" error-code="oops"
       status-code-expression="T(org.springframework.http.HttpStatus).ACCEPTED">
   <request-mapping headers="BAR"/>
</http:inbound-channel-adapter>

<http:inbound-gateway> 從回覆 Messagehttp_statusCode 頭解析 '狀態碼'。從 4.2 版本開始,如果在 reply-timeout 內未收到回覆,則預設響應狀態碼為 500 Internal Server Error。有兩種方法可以修改此行為:

  • 新增 reply-timeout-status-code-expression。這與入站介面卡上的 status-code-expression 具有相同的語義。

  • 新增 error-channel 並返回帶有 HTTP 狀態碼頭的適當訊息,如下例所示:

    <int:chain input-channel="errors">
        <int:header-enricher>
            <int:header name="http_statusCode" value="504" />
        </int:header-enricher>
        <int:transformer expression="payload.failedMessage" />
    </int:chain>

ErrorMessage 的載荷是 MessageTimeoutException。它必須轉換為閘道器可以轉換的內容,例如 String。一個很好的候選項是異常的 message 屬性,當您使用 expression 技術時,使用的就是這個值。

如果在主流程超時後錯誤流程也超時,則返回 500 Internal Server Error;或者如果存在 reply-timeout-status-code-expression,則對其進行評估。

以前,超時的預設狀態碼是 200 OK。要恢復該行為,請設定 reply-timeout-status-code-expression="200"

同樣從 5.4 版本開始,在準備請求訊息時遇到的錯誤(如果提供了錯誤通道)將被髮送到錯誤通道。關於丟擲適當異常的決定應在錯誤流程中透過檢查異常來完成。以前,任何異常都會簡單地丟擲,導致 HTTP 500 伺服器錯誤響應狀態,但在某些情況下,問題可能是由不正確的請求引數引起的,因此應該丟擲帶有 4xx 客戶端錯誤狀態的 ResponseStatusException。有關更多資訊,請參見 ResponseStatusException。傳送到此錯誤通道的 ErrorMessage 包含原始異常作為用於分析的載荷。

URI 模板變數和表示式

透過將 path 屬性與 payload-expression 屬性和 header 元素結合使用,您可以高度靈活地對映入站請求資料。

在以下示例配置中,入站通道介面卡配置為接受使用以下 URI 的請求:

/first-name/{firstName}/last-name/{lastName}

當您使用 payload-expression 屬性時,{firstName} URI 模板變數對映到 Message 載荷,而 {lastName} URI 模板變數對映到 lname 訊息頭,如下例所示:

<int-http:inbound-channel-adapter id="inboundAdapterWithExpressions"
    path="/first-name/{firstName}/last-name/{lastName}"
    channel="requests"
    payload-expression="#pathVariables.firstName">
    <int-http:header name="lname" expression="#pathVariables.lastName"/>
</int-http:inbound-channel-adapter>

有關 URI 模板變數的更多資訊,請參見 Spring 參考手冊中的 uri 模板模式

自 Spring Integration 3.0 以來,除了 payload 和 header 表示式中現有的 #pathVariables#requestParams 變數外,我們還添加了其他有用的表示式變數:

  • #requestParams:來自 ServletRequest parameterMapMultiValueMap

  • #pathVariables:來自 URI 模板佔位符及其值的 Map

  • #matrixVariables:根據 Spring MVC 規範MultiValueMapMap。請注意,#matrixVariables 需要 Spring MVC 3.2 或更高版本。

  • #requestAttributes:與當前請求關聯的 org.springframework.web.context.request.RequestAttributes

  • #requestHeaders:來自當前請求的 org.springframework.http.HttpHeaders 物件。

  • #cookies:來自當前請求的 jakarta.servlet.http.Cookie 例項的 MultiValueMap<String, Cookie>

請注意,如果下游訊息流是單執行緒的並且位於請求執行緒內,則可以透過 ThreadLocal org.springframework.web.context.request.RequestAttributes 變數在表示式中訪問所有這些值(以及其他值)。以下示例配置了一個使用 expression 屬性的轉換器:

<int-:transformer
    expression="T(org.springframework.web.context.request.RequestContextHolder).
                  requestAttributes.request.queryString"/>

出站

要配置出站閘道器,可以使用名稱空間支援。以下程式碼片段顯示了出站 HTTP 閘道器的可用配置選項:

<int-http:outbound-gateway id="example"
    request-channel="requests"
    url="https:///test"
    http-method="POST"
    extract-request-payload="false"
    expected-response-type="java.lang.String"
    charset="UTF-8"
    request-factory="requestFactory"
    reply-timeout="1234"
    reply-channel="replies"/>

最重要的是,請注意提供了 'http-method' 和 'expected-response-type' 屬性。這是兩個最常配置的值。預設的 http-methodPOST,預設的響應型別是 null。如果響應型別為 null,只要 HTTP 狀態碼是成功的(非成功的狀態碼會丟擲異常),回覆 Message 的載荷就會包含 ResponseEntity。如果您期望其他型別,例如 String,請以完全限定類名提供(在前面的示例中是 java.lang.String)。另請參見 HTTP 出站元件 中關於空響應體的注意事項。

從 Spring Integration 2.1 開始,HTTP 出站閘道器的 request-timeout 屬性已重新命名為 reply-timeout,以便更好地反映其意圖。

自 Spring Integration 2.2 以來,預設不再啟用透過 HTTP 進行 Java 序列化。以前,當將 expected-response-type 屬性設定為 Serializable 物件時,Accept 頭沒有正確設定。自 Spring Integration 2.2 以來,SerializingHttpMessageConverter 現在已更新,將 Accept 頭設定為 application/x-java-serialized-object

然而,由於這可能導致與現有應用程式不相容,因此決定不再自動將此轉換器新增到 HTTP 端點。如果您希望使用 Java 序列化,可以透過使用 message-converters 屬性(在 XML 配置中)或使用 setMessageConverters() 方法(在 Java 配置中)將 SerializingHttpMessageConverter 新增到相應的端點。此外,您可能希望考慮使用 JSON,透過在 classpath 中包含 Jackson 庫 即可啟用 JSON。

從 Spring Integration 2.2 開始,您還可以透過使用 SpEL 和 http-method-expression 屬性動態確定 HTTP 方法。請注意,此屬性與 http-method 互斥。您也可以使用 expected-response-type-expression 屬性代替 expected-response-type,並提供任何有效的 SpEL 表示式來確定響應的型別。以下配置示例使用 expected-response-type-expression

<int-http:outbound-gateway id="example"
    request-channel="requests"
    url="https:///test"
    http-method-expression="headers.httpMethod"
    extract-request-payload="false"
    expected-response-type-expression="payload"
    charset="UTF-8"
    request-factory="requestFactory"
    reply-timeout="1234"
    reply-channel="replies"/>

如果您的出站介面卡以單向方式使用,則可以使用 outbound-channel-adapter 代替。這意味著成功的響應會執行,而不會向回覆通道傳送任何訊息。在任何非成功的響應狀態碼的情況下,它都會丟擲異常。配置看起來與閘道器非常相似,如下例所示:

<int-http:outbound-channel-adapter id="example"
    url="https:///example"
    http-method="GET"
    channel="requests"
    charset="UTF-8"
    extract-payload="false"
    expected-response-type="java.lang.String"
    request-factory="someRequestFactory"
    order="3"
    auto-startup="false"/>

要指定 URL,可以使用 'url' 屬性或 'url-expression' 屬性。'url' 屬性接受一個簡單的字串(帶有 URI 變數的佔位符,如下所述)。'url-expression' 是一個 SpEL 表示式,以 Message 作為根物件,這使得 URL 具有動態性。表示式求值後生成的 URL 仍然可以包含 URI 變數的佔位符。

在以前的版本中,一些使用者使用佔位符將整個 URL 替換為 URI 變數。Spring 3.1 中的更改可能會導致一些跳脫字元(例如 '?')出現問題。因此,我們建議,如果您希望在執行時完全生成 URL,請使用 'url-expression' 屬性。

對映 URI 變數

如果您的 URL 包含 URI 變數,可以使用 <uri-variable> 元素對其進行對映。此元素可用於 HTTP 出站閘道器和 HTTP 出站通道介面卡。以下示例將 zipCode URI 變數對映到表示式:

<int-http:outbound-gateway id="trafficGateway"
    url="https://local.yahooapis.com/trafficData?appid=YdnDemo&amp;zip={zipCode}"
    request-channel="trafficChannel"
    http-method="GET"
    expected-response-type="java.lang.String">
    <int-http:uri-variable name="zipCode" expression="payload.getZip()"/>
</int-http:outbound-gateway>

<uri-variable> 元素定義兩個屬性:nameexpressionname 屬性標識 URI 變數的名稱,而 expression 屬性用於設定實際值。透過使用 expression 屬性,您可以充分利用 Spring Expression Language (SpEL) 的強大功能,該功能使您能夠完全動態地訪問訊息載荷和訊息頭。例如,在前面的配置中,對 Message 的載荷物件呼叫 getZip() 方法,該方法的結果用作名為 'zipCode' 的 URI 變數的值。

自 Spring Integration 3.0 以來,HTTP 出站端點支援 uri-variables-expression 屬性,用於指定一個表示式,該表示式應被求值,從而生成一個包含 URL 模板中所有 URI 變數佔位符的 Map。它提供了一種機制,您可以在其中根據出站訊息使用不同的變量表達式。此屬性與 <uri-variable/> 元素互斥。以下示例顯示瞭如何使用 uri-variables-expression 屬性:

<int-http:outbound-gateway
     url="https://foo.host/{foo}/bars/{bar}"
     request-channel="trafficChannel"
     http-method="GET"
     uri-variables-expression="@uriVariablesBean.populate(payload)"
     expected-response-type="java.lang.String"/>

uriVariablesBean 可能定義如下:

public class UriVariablesBean {
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    public Map<String, ?> populate(Object payload) {
        Map<String, Object> variables = new HashMap<String, Object>();
        if (payload instanceOf String.class)) {
            variables.put("foo", "foo"));
        }
        else {
            variables.put("foo", EXPRESSION_PARSER.parseExpression("headers.bar"));
        }
        return variables;
    }

}
uri-variables-expression 必須求值為 MapMap 的值必須是 StringExpression 的例項。將此 Map 提供給 ExpressionEvalMap,以便在出站 Message 的上下文中進一步解析 URI 變數佔位符。

重要提示 uriVariablesExpression 屬性提供了一種非常強大的機制來評估 URI 變數。我們預計人們主要使用簡單的表示式,例如前面的示例。但是,您也可以配置類似 "@uriVariablesBean.populate(#root)" 的內容,返回的 map 中的表示式可以是 variables.put("thing1", EXPRESSION_PARSER.parseExpression(message.getHeaders().get("thing2", String.class)));,其中表達式在名為 thing2 的訊息頭中動態提供。由於頭可能來自不可信的來源,HTTP 出站端點在評估這些表示式時使用 SimpleEvaluationContextSimpleEvaluationContext 僅使用 SpEL 特性的一個子集。如果您信任您的訊息來源並希望使用受限的 SpEL 結構,請將出站端點的 trustedSpel 屬性設定為 true

您可以使用自定義 url-expression 和一些用於構建和編碼 URL 引數的工具來實現需要按訊息提供動態 URI 變數集的場景。以下示例顯示瞭如何實現:

url-expression="T(org.springframework.web.util.UriComponentsBuilder)
                           .fromHttpUrl('https://HOST:PORT/PATH')
                           .queryParams(payload)
                           .build()
                           .toUri()"

queryParams() 方法期望一個 MultiValueMap<String, String> 作為引數,因此您可以在執行請求之前提前構建一組真實的 URL 查詢引數。

整個 queryString 也可以作為 uri-variable 呈現,如下例所示:

<int-http:outbound-gateway id="proxyGateway" request-channel="testChannel"
              url="http://testServer/test?{queryString}">
    <int-http:uri-variable name="queryString" expression="'a=A&amp;b=B'"/>
</int-http:outbound-gateway>

在這種情況下,您必須手動提供 URL 編碼。例如,您可以使用 org.apache.http.client.utils.URLEncodedUtils#format() 來達到此目的。如前所述,可以使用以下 Java Streams 片段將手動構建的 MultiValueMap<String, String> 轉換為 List<NameValuePair> format() 方法引數:

List<NameValuePair> nameValuePairs =
    params.entrySet()
            .stream()
            .flatMap(e -> e
                    .getValue()
                    .stream()
                    .map(v -> new BasicNameValuePair(e.getKey(), v)))
            .collect(Collectors.toList());

控制 URI 編碼

預設情況下,在傳送請求之前,URL 字串會被編碼(參見 UriComponentsBuilder)為 URI 物件。在某些非標準 URI 的場景中(例如 RabbitMQ REST API),進行編碼是不希望的。<http:outbound-gateway/><http:outbound-channel-adapter/> 提供一個 encoding-mode 屬性。要停用 URL 編碼,請將此屬性設定為 NONE(預設為 TEMPLATE_AND_VALUES)。如果您希望部分編碼 URL 的某些部分,請在 <uri-variable/> 中使用 expression,如下例所示:

<http: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!')"/>
</http:outbound-gateway>

使用 Java DSL,此選項可以透過 BaseHttpMessageHandlerSpec.encodingMode() 選項進行控制。相同的配置也適用於 WebFlux 模組Web 服務模組 中類似的出站元件。對於更復雜的場景,建議在外部提供的 RestTemplate 上配置 UriTemplateHandler;或者在 WebFlux 的情況下,在 WebClient 上配置其 UriBuilderFactory