為 API Proxy 新增 CORS 支援

您正在查看 Apigee Edge 說明文件。
查看 Apigee X 說明文件
資訊

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 新增 CORS 支援,您可以在建立 API Proxy 時將「新增 CORS」政策附加至 API Proxy。如要新增這項政策,請在「建構 Proxy」精靈的「安全性」頁面中,勾選「新增 CORS 標頭」核取方塊。

當您勾選這個核取方塊時,系統會自動將名為「Add CORS」的政策新增至系統,並附加至目標端點回應預先流程,如下圖所示:

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

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

您必須修改政策,如下所示:

  • Access-Control-Allow-Headers 標頭中加入 content-typeauthorization 標頭 (支援基本驗證或 OAuth2 的必備條件),如以下程式碼片段所示。
  • 針對 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 錯誤:標頭含有多個值「*、*」,但只允許一個值

<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>

將 CORS 標頭新增至現有的 Proxy

您必須手動建立新的「指派訊息」政策,並將上一節所列的「新增 CORS」政策程式碼複製到該政策。接著,將政策附加至 API Proxy 目標端點的回應預先流動。您可以視需要修改標頭值。如要進一步瞭解如何建立及附加政策,請參閱什麼是政策?

處理 CORS 預檢要求

CORS 預檢是指向伺服器傳送要求,確認伺服器是否支援 CORS。一般預檢回應包括伺服器接受來自哪些來源的 CORS 要求、CORS 要求支援的 HTTP 方法清單、可用於資源要求中的標頭、快取最長回應時間等等。如果服務不支援 CORS 支援,或不想接受來自用戶端來源的跨來源要求,系統會強制執行瀏覽器的跨來源政策,而且用戶端提出的跨網域要求會與該伺服器中代管的資源互動。

一般來說,CORS 預檢要求是透過 HTTP OPTIONS 方法提出。支援 CORS 的伺服器收到 OPTIONS 要求時,會將一組 CORS 標頭傳回給用戶端,藉此指出其 CORS 支援等級。完成這類握手之後,用戶端知道可以向非來源網域要求哪些內容。

如要進一步瞭解預檢,請參閱跨源資源共享 W3C 建議。這裡還有許多關於 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>
    
  • 系統會建立 OptionsPreFlight 流程,以便在接收到 OPTIONS 要求,且 Origin 和 Access-Control-Request-Method 要求標頭並非空值時,將新增 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-F 低 README 檔案。