為 API Proxy 新增 CORS 支援

您目前查看的是 Apigee Edge 說明文件。
前往 Apigee X 說明文件
info

CORS (跨源資源共享) 是一種標準機制,可讓您在網頁中執行 JavaScript XMLHttpRequest (XHR) 呼叫,以便與非來源網域的資源進行互動。CORS 是常見的解決方案,可因應所有瀏覽器強制執行的「同源政策」。舉例來說,如果您從瀏覽器執行的 JavaScript 程式碼,對 Twitter API 進行 XHR 呼叫,該呼叫就會失敗。這是因為向瀏覽器提供網頁的網域,與提供 Twitter API 的網域不同。CORS 允許伺服器「選擇加入」跨源資源共用,藉此解決這個問題。

影片:觀看短片,瞭解如何在 API Proxy 上啟用 CORS。

CORS 的典型用途

下列 JQuery 程式碼會呼叫虛構的目標服務。如果從瀏覽器 (網頁) 的環境中執行,由於同源政策,呼叫會失敗:

<script>
var url = "http://service.example.com";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This is where we end up!
            }
    });
  });
});
</script>

解決這個問題的方法之一,是建立 Apigee API Proxy,在後端呼叫服務 API。請注意,Edge 位於用戶端 (本例中為瀏覽器) 和後端 API (服務) 之間。由於 API Proxy 是在伺服器上執行,而非在瀏覽器中,因此能夠順利呼叫服務。接著,您只需要將 CORS 標頭附加至 TargetEndpoint 回應。只要瀏覽器支援 CORS,這些標頭就會向瀏覽器發出訊號,表示可以「放寬」同源政策,允許跨源 API 呼叫成功。

建立支援 CORS 的 Proxy 後,您可以在用戶端程式碼中呼叫 API Proxy 網址,而非後端服務。例如:

<script>
var url = "http://myorg-test.apigee.net/v1/example";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This time, we do not end up here!
            }
    });
  });
});
</script>

將「新增 CORS」政策附加至新的 API Proxy

建立 API Proxy 時,將「Add CORS」政策附加至 API Proxy,即可為 API Proxy 新增 CORS 支援。如要新增這項政策,請在「Build a Proxy」精靈的「Security」頁面中,選取「Add CORS headers」核取方塊。

選取這個核取方塊後,系統會自動新增名為「Add CORS」的政策,並附加至 TargetEndpoint 回應預先流程,如下圖所示:

在「Policies」下方的導覽器中新增 CORS 政策,並附加至右側窗格的 TargetEndpoint 回應預先流程

「新增 CORS 政策」會以 AssignMessage 政策的形式實作,在回應中新增適當的標頭。基本上,標頭會讓瀏覽器知道要與哪些來源共用資源、接受哪些方法等等。如要進一步瞭解這些 CORS 標頭,請參閱跨源資源共享 W3C 建議

請按照下列方式修改政策:

  • content-typeauthorization 標頭 (支援基本驗證或 OAuth2 時必須使用) 新增至 Access-Control-Allow-Headers 標頭,如下方程式碼片段所示。
  • 如要使用 OAuth2 驗證,您可能需要採取步驟,修正不符合 RFC 的行為
  • 建議您使用 <Set> 設定 CORS 標頭,而非 <Add>,如下方摘錄內容所示。 使用 <Add> 時,如果 Access-Control-Allow-Origin 標頭已存在,您會收到下列錯誤:

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

    詳情請參閱「 CORS Error : header contains multiple values '*, *', but only one is allowed」。

<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

在現有 Proxy 中新增 CORS 標頭

您需要手動建立新的 Assign Message 政策,並將上一節列出的 Add CORS 政策程式碼複製到其中。然後,將政策附加至 API Proxy 的 TargetEndpoint 回應預先流程。您可以視需要修改標頭值。如要進一步瞭解如何建立及附加政策,請參閱「什麼是政策?」。

處理 CORS 預檢要求

CORS 預檢是指將要求傳送至伺服器,確認伺服器是否支援 CORS。典型的預檢回應包括伺服器會接受哪些來源的 CORS 要求、CORS 要求支援的 HTTP 方法清單、可用於資源要求中的標頭、預檢回應的快取時間上限等。如果服務未指出支援 CORS,或不希望接受來自用戶端來源的跨源要求,瀏覽器就會強制執行跨源政策,且用戶端發出的任何跨網域要求都會失敗,無法與該伺服器上託管的資源互動。

一般來說,CORS 預檢要求會使用 HTTP OPTIONS 方法。支援 CORS 的伺服器收到 OPTIONS 要求時,會傳回一組 CORS 標頭給用戶端,指出其 CORS 支援等級。完成交握後,用戶端就會知道允許從非原始網域要求哪些內容。

如要進一步瞭解預檢,請參閱 Cross-Origin Resource Sharing W3C Recommendation。此外,您也可以參考許多關於 CORS 的網誌和文章。

Apigee 不會隨附 CORS 預檢解決方案,但您可以實作,如本節所述。目標是讓 Proxy 在條件式流程中評估 OPTIONS 要求。Proxy 接著會將適當的回應傳回給用戶端。

讓我們看看範例流程,然後討論處理預檢要求的各個部分:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request/>
            <Response>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Response>
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
        </Flow>
    </Flows>

    <PreFlow name="PreFlow">
        <Request/>
        <Response/>

    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

這個 ProxyEndpoint 的主要部分如下:

  • 系統會為 OPTIONS 要求建立具有條件的 NULL 目標 RouteRule。請注意,系統未指定 TargetEndpoint。如果收到 OPTIONS 要求,且 Origin 和 Access-Control-Request-Method 要求標頭不是空值,Proxy 會立即在回應中將 CORS 標頭傳回給用戶端 (略過實際的預設「後端」目標)。如要進一步瞭解流程條件和 RouteRule,請參閱「含有流程變數的條件」。

    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
  • 如果收到 OPTIONS 要求,且 Origin 和 Access-Control-Request-Method 要求標頭不是空值,系統就會建立 OptionsPreFlight 流程,將含有 CORS 標頭的「新增 CORS 政策」新增至流程。

     <Flow name="OptionsPreFlight">
                <Request/>
                <Response>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Response>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>

使用範例 CORS 解決方案

您可以在 GitHub 上找到以共用流程實作的 CORS 解決方案範例。 將共用流程套件匯入環境,並使用流程掛鉤附加,或直接附加至 API Proxy 流程。詳情請參閱範例隨附的 CORS-Shared-FLow README 檔案。