由於安全上的考量,預設透過 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 。
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 出現以下的錯誤訊息。
Web.Config 自訂 Header
如果要讓所有的來源都允許,只需在 web.config 裡面設定自訂的 Header 就能夠通了。
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*"/>
</customHeaders>
</httpProtocol>
</system.webServer>
但是,這樣一來,任何來源要使用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>
但是這樣一來,兩個只有一個能夠運作
那麼如何才設定兩個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"/>
直接加兩個,結果連編譯都過不了
所以多組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查看
在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沒有設定,將被拒絕
↑↓有設定在內的,沒問題,可以正常存取WebAPI
↓沒有設定在內的,被拒絕
總結:
小喵這個需求,是希望整個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
^_^
以下是簽名:
- 歡迎轉貼本站的文章,不過請在貼文主旨上加上【轉貼】,並在文章中附上本篇的超連結與站名【topcat姍舞之間的極度凝聚】,感恩大家的配合。
- 小喵大部分的文章會以小喵熟悉的語言VB.NET撰寫,如果您需要C#的Code,也許您可以試著用線上的工具進行轉換,這裡提供幾個參考
Microsoft MVP Visual Studio and Development Technologies (2005~2019/6) | topcat Blog:http://www.dotblogs.com.tw/topcat |