SignalR的跨網域支援
在SignalR JS Client Hubs文件Cross Domain Support一節看到SignalR可利用JSONP實現跨網域呼叫。簡單的說,就是將SignalR Hub架在A網站,卻從B網站的網頁執行$.connection.hub.start()連上A網站的SignalR接收資料或傳送指令。乍看稀鬆平常,但網站寫多一點的老鳥都知道這個副本裡有個XHR跨網站存取的小王要解決,所幸SignalR已內建跨網域支援,所以我們就來寫幾行程式玩看看。
以下是一個獨立網頁,目前在扮演先前利用SignalR實現遠端程式遙控功能文章裡SignalRClient.exe的角色:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>SignalR Cross-Site Web Page Client</title>
<base href="http://localhost:19636" />
<script src="/Scripts/jquery-1.6.4.js" type="text/javascript"></script>
<script src="/Scripts/jquery.signalR-0.5.2.js" ></script>
<script src="/SignalR/hubs" ></script>
<script>
$(function () {
//與SignalR Hub建立連線
var commHub = $.connection.commHub;
//實做ShowMessage(msg)
commHub.ShowMessage = function(msg) {
$("body").append("<div>" + msg + "</div>");
};
//跨網域引用SignalR時,要設定hub.url, 並hub.start({ jsonp: true });
$.connection.hub.url = "http://127.0.0.1:19636/signalr";
$.connection.hub.start({ jsonp: true })
.done(function() {
commHub.register("XSS");
});
});
</script>
</head>
<body>
</body>
</html>
將以上網頁SignalRClient.htm存在本機隨意資料夾下(或放到其他網站也行,只要不在CommHub所在的ASP.NET MVC網站上就會產生跨網域障礙),使用IE等瀏覽器開啟該網頁進行測試,則可在主控網頁看到其所註冊的名稱--XSS,也能傳送訊息顯示在該網頁上,實現了與SignalRClient.exe相似的功能。檢視該網頁的HTML原始碼,可發現SignalR JS Client是以<script>方式建立連線[見圖標(1)],其src為httq://127.0.0.1:19636/signalr/connect?transport=longPolling&connectionId=81b9...略...62b98&connectionData=%5B%7B%22name%22%3A%22CommHub%22%7D%5D&tid=4&callback=jQuery16407549852419734552_1341978125354&_=1341978125642,看到callback=jQuery164075…參數,判斷是透過jQuery.ajax()的jsonp dataType進行跨網域呼叫,而由[圖標(2)]的回傳結果也可驗證此點。最後,主控端送來的文字就順利呈現在網頁上囉! (見圖標(3),測試時我按了兩次Send,故文字出現兩次) 驗證了SignalR JavaScript Client的跨網域支援能力。

【後記】
測試過程發現IE9即使不啟甪{ jsonp: true }也能跨網域運作,但在Chrome上卻不然。追進原始碼,發現SignalR支援webSocket及longPolling兩種傳輸模式,而start()中有一小段邏輯,當發現URL跨網域而瀏覽器的XHR不支援Cross-Original Request Sharing時(簡稱CORS, SignalR透過$.support.cors偵測,IE9被判定不支援),會自動切換為jsonp=true,改用longPolling + JSONP克服障礙;而在不啟用JSONP的前題下,要做到跨網域XHR,則需要從Server端額外加入一些Access-Control-* Header[參考]。
由此可解釋未指定{ json: true }時,IE9因被強制切成jsonp=true而成功,Chrome未被切成jsonp模式,又因SignalR端又未加入CORS需要Header,故無法順利運作。