跨域問題來源於JavaScript的同源策略,即只有 協議+主機名+埠號 (如存在)相同,則允許相互訪問。也就是說JavaScript只能訪問和操作自己域下的資源,不能訪問和操作其他域下的資源。
在以前,前端和後端混雜在一起, 比如JavaScript直接呼叫同系統裡面的一個Httphandler,就不存在跨域的問題,但是隨著現代的這種多種客戶端的流行,比如一個應用通常會有Web端,App端,以及WebApp端,各種客戶端通常會使用同一套的後臺處理邏輯,即API, 前後端分離的開發策略流行起來,前端只關註展現,通常使用JavaScript,後端處理邏輯和資料通常使用WebService來提供json資料。一般的前端頁面和後端的WebService API通常部署在不同的伺服器或者域名上。這樣,透過ajax請求WebService的時候,就會出現同源策略的問題。
需要說明的是,同源策略是JavaScript裡面的限制,其他的程式語言,比如在C#,Java或者iOS等其他語言中是可以呼叫外部的WebService,也就是說,如果開發Native應用,是不存在這個問題的,但是如果開發Web或者Html5如WebApp,通常使用JavaScript ajax對WebService發起請求然後解析傳回的值,這樣就可能存在跨域的問題。
一般的,很容易想到,將外部的資源搬到同一個域上就能解決同源策略的限制的。即在Web網站上同時開發一個Http服務端頁面,所有JavaScript的請求都發到這個頁面上來,這個頁面在內部使用其他語言去呼叫外部的WebService。即新增一個代理層。這種方式可以解決問題,但是不夠直接和高效。
目前,比較常見的跨域解決方案包括JSONP (JSON with padding)和CORS (Cross-origin resource sharing )。一些解決方案需要客戶端和服務端配合如JSOP,一些則只需要服務端配合處理比如CORS。下麵分別介紹這兩種跨域方案,以及服務端WebService如何支援這兩種跨域方案。
JSONP以及WebService的支援
同源策略下,某個伺服器是無法獲取到伺服器以外的資料,但是html裡面的img,iframe和script等標簽是個例外,這些標簽可以透過src屬性請求到其他伺服器上的資料。而JSONP就是透過script節點src呼叫跨域的請求。
當我們向伺服器提交一個JSONP的請求時,我們給服務傳了一個特殊的引數,告訴服務端要對結果特殊處理一下。這樣服務端傳回的資料就會進行一點包裝,客戶端就可以處理。
舉個例子,服務端和客戶端約定要傳一個名為callback的引數來使用JSONP功能。比如請求的引數如下:
http://www.example.net/sample.aspx?callback=mycallback
如果沒有後面的callback引數,即不使用JSONP的樣式,該服務的傳回結果可能是一個單純的json字串,比如:
{ foo : ‘bar’ }
如果和服務端約定jsonp格式,那麼服務端就會處理callback的引數,將傳回結果進行一下處理,比如處理成:
mycallback({ foo : ‘bar’ })
可以看到,這其實是一個函式呼叫,比如可以實現在頁面定義一個名為mycallback的回呼函式:
mycallback = function(data)
{
alert(data.foo);
};
現在,請求的傳回值回去觸發回呼函式,這樣就完了了跨域請求。
如果使用ServiceStack建立WebService的話,支援Jsonp方式的呼叫很簡單,只需要在AppHost的Configure函式裡面註冊一下對響應結果進行過濾處理即可。
///
/// Application specific configuration /// This method should initialize any IoC resources utilized by your web service classes.
///
///
public override void Configure(Container container)
{
ResponseFilters.Add((req, res, dto) =>
{
var func = req.QueryString.Get(“callback”);
if (!func.isNullOrEmpty())
{
res.AddHeader(“Content-Type”, ContentType.Html);
res.Write(“”
.FormatWith(func, dto.ToJson()));
res.Close();
}
});
}
JSONP跨域方式比較方便,也支援各種較老的瀏覽器,但是缺點很明顯,他只支援GET的方式提交,不支援其他Post的提交,Get方式對請求的引數長度有限制,在有些情況下可能不滿足要求。所以下麵就介紹一下CORS的跨域解決方案。
CORS跨域及WebService的支援
先來看一個例子,我們新建一個基本的html頁面,在裡面編寫一個簡單的是否支援跨域的小指令碼,如下:
AJAX跨域請求測試