一步一步學習SignalR進行實時通信_3_通過CORS解決跨域
標簽(空格分隔): SignalR
前言
這周工作比較忙,一直沒有時間學習SignalR,大致希望一周能寫一篇關於SignalR的文章。上一篇用Persistent Connections方式實現了個簡單的在線聊天功能,這次我們繼續深入學習。
關於start()的補充
在上一篇文章里前台的html頁面我們通過幾句javascript創建了一個,代碼如下,也可以下載上次的源碼。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>persistent connections</title>
<script src="Scripts/jquery-1.10.2.min.js"></script>
<script src="Scripts/jquery.signalR-2.0.0.min.js"></script>
</head>
<body>
<h1>Echo service</h1>
<div>
<input type="text" id="text" />
<button id="send">Send</button>
</div>
<script>
$(function () {
var connection = $.connection("/echo");
connection.logging = true;
connection.received(function (data) {
$("body").append(data + "<br />");
});
connection.error(function (err) {
alert("存在一個錯誤. \n" +
"Error: " + err.message);
});
connection.start().done(function () {
$("#send").click(function () {
connection.send($("#text").val());
$("#text").val("").focus();
});
});
});
</script>
</body>
</html>
這里需要做些說明:通過代碼var connection = $.connection("/echo");
我們創建了一個連接,通過connection.start().done()
來開啟連接並在連接完成時處理我們需要處理的事件。
如果你將以下代碼
connection.start().done(function () {
connection.send('Hi');
});
分成2部分寫,如:
connection.start();
connection.send('Hi');
那么你必須注意:
雖然你在connection.send()
之前調用了connection.start()
開啟了連接,但是connection.start()
是一個__異步__方法,意味着有可能你在調用connection.send()
時connection並未開啟,那么項目將會報錯。
正確方法如之前代碼所示,再加上一段開啟失敗的代碼:
var connection = $.connection("/echo");
connection.start(function() {
// 連接開啟成功才會進入這里
connection.send("Hi");
}).fail(function() {
//連接開啟失敗則進入這里
alert("服務開啟失敗");
});
跨域解決方案
上篇文章里有同學問到跨域的問題,那么在接下來的時間我將會帶着大家一起學習。
在SignalR解決跨域,有兩種方式:第一種是JSONP,第二種是CORS。
JSONP
如果你的服務必須要支持老版本的瀏覽器,那么JSONP是唯一選擇,否則處於安全的考慮這並不被推薦,具體什么安全因素我並不知曉(有同學知道的話可以說明下),基於快速學習的情況下,我們無需糾纏於此。服務器默認會禁用此功能,我們可以通過初始化時提供一個ConnectionConfiguration對象並設置EnableJSONP屬性為true來激活此功能。
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new ConnectionConfiguration()
{
EnableJSONP = true
};
app.MapSignalR<EchoConnection>("/echo", config);
}
}
我想這幾句代碼應該很好理解,我們在前面提到過MapSignalR<TConnection>()
有許多重載方法,這是另一個重載方法通過提供一個ConnectionConfiguration
對象進行相關配置。
CORS
CORS是一個獨立的框架,它可以很方便的解決跨域問題,通過Nuget安裝
安裝命令:Install-Package microsoft.owin.cors
CORS是通過Owin實現的,所以我們需要在項目啟動時對他進行一些配置,和做SignalR映射一樣實在Startup中進行配置。
public class Startup
{
public void Configuration(IAppBuilder app)
{
//app.MapSignalR<EchoConnection>("/echo");
app.Map("/echo",
map => {
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR<EchoConnection>();
}
);
}
}
代碼應該也不難,這次我們通過app.Map()
進行映射,第一個參數是映射的地址,第二個參數是一個lambda表達式,通過UseCors(CorsOptions.AllowAll)
允許允許跨域。
CORS跨域演示
JSONP我不做演示了有興趣的可以自己嘗試下,接下來我這里做一個通過CORS來進行跨域聊天。首先我講上次的項目復制一份,省得再重新打代碼,並將__復制__出來的項目名稱由__SignalR_1__改為__SignalR_2_CORS__。
項目目錄如下圖所示:
省得麻煩,我把SignalR_1部署在IIS上面,這就充當了 一個遠程的SignalR服務。
部署成功后,如圖所示:
此時我們講SignalR_2_CORS項目稍作修改
- 將Startup中的映射刪去,此時SignalR_2_CORS已不做SignalR服務器了,只做客戶端來連接SignalR_1提供的服務
- 將echo中的
var connection = $.connection("/echo");
改為
var connection = $.connection("http://127.0.0.1:8083/echo");
然后運行SignalR_2_CORS中的echo頁面,結果如圖所示:
出現了一個錯誤,這個錯誤提示是我們自己寫的
因為我們的__SignalR_1__並沒有允許跨域連接,那么在__SignalR_2_CORS__中當然無法連接,接下來我們在項目一中允許跨域連接。
重新編譯項目一后再刷新下__SignalR_1__的_echo.html_頁面,注意我們這個頁面地址
然后刷新下__SignalR_2_CORS__的頁面,注意這個地址
連接成功,沒有報錯了,發送個消息試試看(●ˇ∀ˇ●)
結束語
這里通過CORS實現了SignalR的跨域問題,跨域如此簡單趕快試試吧 。
注意:在通過Nuget安裝CORS包時,我這邊提示了Unable to find package 'Microsoft.AspNet.Cors'我已經翻牆了,所以這個應該不是需要翻牆才能下載,但是在Nuget頁面中搜索確實有這個包,具體什么原因引起的我也不清楚,如果你有碰到這個問題請下載解壓並添加引用即可,由於
Microsoft.AspNet.Cors
依賴於Microsoft.AspNet.Cors
,所以里面的2個包都要添加引用