HTTP 命名空間支援

Spring Integration 提供 http 命名空間和對應的綱要定義。若要將其包含在您的設定中,請在您的應用程式內容設定檔中提供下列命名空間宣告

<?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 輸入通道配接器的剖析會註冊類型為 IntegrationRequestMappingHandlerMappingintegrationRequestMappingHandlerMapping bean,以防尚未註冊。此 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> 元素可讓您為相同的 path (甚至相同的 supported-methods) 設定多個 Spring Integration HTTP 輸入端點,並讓您根據傳入的 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 請求對應設定相同的路徑 (例如,一個中的 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 標頭解析 'status code'。從 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。一個好的候選者是例外狀況的訊息屬性,這是您使用 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 以來,除了現有的 #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://127.0.0.1/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 回應類型,回覆 Message 的酬載包含 ResponseEntity,只要其 HTTP 狀態為成功 (不成功狀態碼會擲回例外狀況)。如果您預期不同的類型,例如 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,這可以透過在類別路徑上擁有 Jackson 程式庫 來啟用。

從 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://127.0.0.1/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://127.0.0.1/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) 的強大功能,這讓您可以完全動態地存取訊息酬載和訊息標頭。例如,在先前的組態中,getZip() 方法會在 Message 的酬載物件上調用,而該方法的結果會用作名為 'zipCode' 的 URI 變數的值。

自 Spring Integration 3.0 以來,HTTP 輸出端點支援 uri-variables-expression 屬性,以指定應評估的 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() 來達到此目的。如先前所述,手動建構的 MultiValueMap<String, String> 可以透過使用以下 Java Streams 代码片段轉換為 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 Services 模組中的類似輸出元件。對於更複雜的情況,建議在外部提供的 RestTemplate 上組態 UriTemplateHandler;或者在 WebFlux 的情況下,使用具有其 UriBuilderFactoryWebClient