[WebAPI][CORS] WebAPI 設定多組(Multiple) 跨 Domain

由於安全上的考量,預設透過 AJAX 跨 Domain 存取 WebAPI 是被禁止的,而要處理這部分的問題,除了 JSONP 以外,另外就是 W3C 定義的 Response Header 【Access-Control-Allow-Origin】可以解決這個問題,不過經測試結果,如果設定在Web.Config裡面,除了設定為【*】,讓所有的都可以用以外,就只能設定一個 Domain 。不過,實際上可能會需要有幾組 Domain 可以存取我們寫好的 WebAPI。那麼這樣的需求要怎麼處理呢?

緣起

由於安全上的考量,預設透過 AJAX 跨 Domain 存取 WebAPI 是被禁止的,而要處理這部分的問題,除了 JSONP 以外,另外就是 W3C 定義的 Response Header 【Access-Control-Allow-Origin】可以解決這個問題,不過經測試結果,如果設定在Web.Config裡面,除了設定為【*】,讓所有的都可以用以外,就只能設定一個 Domain 。不過,實際上可能會需要有幾組 Domain 可以存取我們寫好的 WebAPI。那麼這樣的需求要怎麼處理呢?

 

測試環境準備

為了測試跨Domain呼叫WebAPI,小喵準備三個專案,分別為1個 WebAPI 專案,另外兩個用空的網站,空網站中分別新增一個 html ,透過 jQuery 的 $.getJSON ,呼叫 WebAPI 的預設 Values 。

001

    T1<br />
    <input type="button" id="btn1" name="btn1" value="Get Values" /><br />
    <span id="span1"></span>
               <!--Script-->
    <script src="Scripts/jquery-2.0.3.min.js"></script>
    <script>
        $(function () {
            $('#btn1').click(function () {
                var strURL = 'http://localhost:50848/api/values'
                $.getJSON(strURL, null, function (rtnData) {
                    $('#span1').text(rtnData);
                });
            });
        });
    </script>

 

↓預設跨 Domain 的 Ajax 被禁止,在 Chrome 的 Console 出現以下的錯誤訊息。

002

 003

 

Web.Config 自訂 Header

如果要讓所有的來源都允許,只需在 web.config 裡面設定自訂的 Header 就能夠通了。

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="*"/>
    </customHeaders>
  </httpProtocol>
</system.webServer>

004

 

005

但是,這樣一來,任何來源要使用WebAPI都可以用,如果是公開的WebAPI那沒問題,但是如果是希望只有某一個,甚至某些網站才能使用,就不能只是【Access-Control-Allow-Origin:*】,此時指定一個Domain自然沒問題

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="http://localhost:21259"/>
    </customHeaders>
  </httpProtocol>
</system.webServer>

但是這樣一來,兩個只有一個能夠運作

 

006

007

那麼如何才設定兩個Domain都可以呢?

小喵試過以下的方式,都不得要領,無法運作

用,區隔,不可行

<add name="Access-Control-Allow-Origin" value="http://localhost:21259,http://localhost:21260"/>

 

用;區隔,不可行

<add name="Access-Control-Allow-Origin" value="http://localhost:21259;http://localhost:21260"/>

 

用空白區隔,不可行

<add name="Access-Control-Allow-Origin" value="http://localhost:21259 http://localhost:21260"/>

直接加兩個,結果連編譯都過不了

 

008

 

所以多組Domain看來在Web.conig裡面是不合適,必須另外想辦法。

 

觀察,找方法

自訂Header,並且可以通用於全專案所有WebAPI的方式,除了在Web.Config以外,其實也可以在Global.asax裡面。

試想,如果可以取得Client端本來的Doamin,然後與小喵準備好的多組Domain一一比對,如果有符合的,再指定Access-Control-Allow-Origin回傳Client端的Domain,如此一來,就可以讓WebAPI對應多組Domain。

接下來,就是如何去取得【Client端本來的Doamin】

首先觀察,當使用$.getJSON的時候,傳遞的內容中,是否有【Client端本來的Doamin】,透過Chrome的NetWork查看

009

在Request的Header裡面,Origin,就是我們要找的【Client端本來的Doamin】,看來這樣一來,就有希望可以解決我們的問題了。

 

解決方式:Global.asax比對傳回

既然有了方向,就來寫程式處理囉,於是在Global.asax中,小喵加上了以下這段

Private Sub WebApiApplication_BeginRequest(sender As Object, e As EventArgs) Handles Me.BeginRequest

    '定義從Client Reguest來的Origin
    Dim ClientOrigin As String = ""
    '透過Request Headers取得Origin
    ClientOrigin = HttpContext.Current.Request.Headers.Item("Origin")

    '定義允許的Domain
    Dim AllowDomains As New List(Of String)
    AllowDomains.Add("http://localhost:21259")
    AllowDomains.Add("http://localhost:21260")

    '定義是否允許CROS的布林,預設為False
    Dim SetCORS As Boolean = False
    '逐一比對
    For Each ad As String In AllowDomains
        If ad = ClientOrigin Then
            '比對正確,設定允許CORS
            SetCORS = True
        End If
    Next

    If SetCORS Then
        '如果允許 CORS ,設定自訂 Header 加上 Access-Control-Allow-Origin 為 Client 端取得的 Orgin
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", ClientOrigin)
    End If

End Sub

 

測試

為了驗證,我們多新增了第三個空網站,並在第三個空的網站中,新增一個HTML,名為T3.html

那麼,預計應該是T1, T2可以正常的運作,而T3沒有設定,將被拒絕

010

↑↓有設定在內的,沒問題,可以正常存取WebAPI

011

 

↓沒有設定在內的,被拒絕

012

 

 

總結:

小喵這個需求,是希望整個WebAPI的專案,可以給多個Domain使用全專案的WebAPI。如果是個別的WebAPI要對應不同的Domain,那麼還是透過Nuget取得CORS的套件來解決比較恰當。

範例中,為了簡單精準的介紹,所以直接寫在程式中,實際運用時,建議將這些設定,寫在資料庫,或者Config檔案中,這樣未來如果有需要增減設定,可以直接修改資料庫或者config設定檔,這樣應用起來會更靈活。

另外,如果是WebAPI 2.0,可以透過Nuget取得支援WebAPI2.0的CORS套件,相關說明請參考以下這篇

http://msdn.microsoft.com/zh-tw/magazine/dn532203.aspx

以上的方式,提供大家參考唷

 

^_^

 


2014 / 1 / 21 補充:
小喵後來又由同事提供了另外一個xdomain的方式
請參考以下這篇:

[WebAPI][CORS]使用 xdomain 實現 WebAPI 多組(Multiple) 跨 Domain

^_^

 


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6)