為 API Proxy 新增 CORS 支援

本頁內容適用於 ApigeeApigee Hybrid

查看 Apigee Edge 說明文件。

CORS (跨源資源共享) 是一種標準機制,可讓您在網頁中執行 JavaScript XMLHttpRequest (XHR) 呼叫,以便與非來源網域的資源進行互動。CORS 是常見的解決方案,可因應所有瀏覽器強制執行的同源政策。舉例來說,如果您從瀏覽器中執行的 JavaScript 程式碼,對 Twitter API 發出 XHR 呼叫,該呼叫就會失敗。這是因為向瀏覽器提供網頁的網域,與提供 Twitter API 的網域不同。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。請注意,Apigee 位於用戶端 (本例中的瀏覽器) 和後端 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 政策附加至 ProxyEndpoint 的要求 PreFlow

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

如要為 API Proxy 新增 CORS 支援,請透過下列方式將「Add CORS」政策附加至 API Proxy:

  • 在「Build a Proxy」精靈的「Security」頁面中,選取「Add CORS headers」核取方塊來建立政策
  • 從「新增政策」對話方塊稍後新增

選取核取方塊新增 CORS 政策時,系統會自動新增名為「Add CORS」的政策,並附加至 TargetEndpoint 要求預先流程。

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

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

  • content-typeauthorization 標頭 (支援基本驗證或 OAuth2 時必須使用) 新增至 Access-Control-Allow-Headers 標頭,如下列程式碼片段所示。
  • 如要使用 OAuth2 驗證,您可能需要採取步驟,修正 不符合 RFC 的行為
<CORS continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <AllowOrigins>{request.header.origin}</AllowOrigins>
    <AllowMethods>GET, PUT, POST, DELETE</AllowMethods>
    <AllowHeaders>origin, x-requested-with, accept, content-type, authorization</AllowHeaders>
    <ExposeHeaders>*</ExposeHeaders>
    <MaxAge>3628800</MaxAge>
    <AllowCredentials>false</AllowCredentials>
    <GeneratePreflightResponse>true</GeneratePreflightResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</CORS>

在現有 Proxy 中新增 CORS 標頭

Cloud 控制台中的 Apigee

如要將 CORS 政策新增至現有的 API Proxy,請按照下列步驟操作:

  1. 在 Google Cloud 控制台中,前往「Proxy Development」>「API Proxies」頁面。

    前往「API Proxies」(API Proxy)

  2. 選取要新增 CORS 政策的 API Proxy。使用者介面會在 Google Cloud 控制台中顯示 Proxy 詳細資料。
  3. 按一下「開發」分頁標籤。
  4. 在導覽窗格中,按一下「政策」列中的「+」按鈕。
  5. 在「建立政策」對話方塊中,按一下「選取政策」欄位,然後向下捲動至「安全性」,並選取「CORS」

  6. 輸入政策詳細資料,然後按一下「建立」

  7. 在導覽窗格中,按一下「Target Endpoints」>「default」下方的「PreFlow」
  8. 在 Visual Editor 的「Request」(要求) 窗格中,按一下「PreFlow」(前置流程) 旁的「+」按鈕。
  9. 在「新增政策步驟」對話方塊中,選取「CORS」CORS政策。
  10. 按一下「新增」附加政策。

傳統版 UI

如要將 CORS 政策新增至現有的 API Proxy,請按照下列步驟操作:

  1. 登入 Apigee 使用者介面
  2. 在左側導覽列中選取「開發」>「API Proxy」
  3. 如果看到「立即試用」按鈕,請按一下該按鈕,顯示新的「開發」檢視畫面。

    「開發」檢視畫面如下所示。

    在 Proxy 編輯器中開發檢視畫面

  4. 選取要新增 CORS 政策的 API Proxy。
  5. 在新的 API Proxy 編輯器中,按一下「Develop」(開發) 分頁:
  6. 在左側的「Navigator」窗格中,按一下「Target Endpoints」>「default」下方的「PreFlow」
  7. 按一下頂端的「+步驟」按鈕,對應「要求前置流程」。畫面會顯示所有可建立的政策,並依類別列出。
  8. 在「安全性」類別中選取「CORS」
  9. 提供名稱,例如 Add CORS,然後按一下「新增」

處理 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>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Request>
            <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>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Request>
                <Response/>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>