ABP module-zero +AdminLTE+Bootstrap Table+jQuery權(quán)限管理系統(tǒng)第十六節(jié)--SignalR與ABP框架Abp.Web.SignalR及擴(kuò)展

SignalR簡介

SignalR是什么?

ASP.NET SignalR 是為 ASP.NET 開發(fā)人員提供的一個(gè)庫,可以簡化開發(fā)人員將實(shí)時(shí) Web 功能添加到應(yīng)用程序的過程。實(shí)時(shí) Web 功能是指這樣一種功能:當(dāng)所連接的客戶端變得可用時(shí)服務(wù)器代碼可以立即向其推送內(nèi)容,而不是讓服務(wù)器等待客戶端請求新的數(shù)據(jù)。

ASP.NET SignalR是ASP.NET開發(fā)人員的一個(gè)新庫,可以使開發(fā)實(shí)時(shí)Web功能變得簡單。SignalR允許服務(wù)器和客戶端之間的雙向通信。服務(wù)器現(xiàn)在可以將內(nèi)容即時(shí)推送到連接的客戶端。SignalR包含用于連接管理(例如連接和斷開事件),連接分組和授權(quán)的API。一般情況下,SignalR會使用JavaScript(Ajax長時(shí)間輪詢)的長輪詢(long polling)的方式來實(shí)現(xiàn)客戶端和服務(wù)器通信,隨著Html5中WebSockets出現(xiàn),SignalR也支持WebSockets通信和支持CORS(跨源資源共享)。另外SignalR開發(fā)的程序不僅僅限制于宿主在IIS中,也可以宿主在任何應(yīng)用程序,包括控制臺,客戶端程序和Windows服務(wù)等,另外還支持Mono,這意味著它可以實(shí)現(xiàn)跨平臺部署在Linux環(huán)境下。JSONP沒有配置,并且連接不是跨域的,如果客戶端和服務(wù)器都支持,則使用WebSocket。

SignalR內(nèi)部有兩類對象:
Http持久連接(Persisten Connection)對象:

  • Connection表示用于發(fā)送單收件人,分組或廣播消息的簡單終端。持久連接API(由PersistentConnection類的.NET代碼表示)為開發(fā)人員提供了對SignalR公開的低級通信協(xié)議的直接訪問。用來解決長時(shí)間連接的功能。還可以由客戶端主動(dòng)向服務(wù)器要求數(shù)據(jù),而服務(wù)器端不需要實(shí)現(xiàn)太多細(xì)節(jié),只需要處理PersistentConnection 內(nèi)所提供的五個(gè)事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。

  • Hub(集線器)對象:基于連接API構(gòu)建的更高級別的管道,允許您的客戶端和服務(wù)器直接調(diào)用彼此的方法。SignalR像魔術(shù)一樣處理跨機(jī)器邊界的調(diào)度,允許客戶端像本地方法一樣方便地調(diào)用服務(wù)器上的方法,反之亦然。對于使用遠(yuǎn)程調(diào)用API(如.NET Remoting)的開發(fā)人員來說,使用Hubs通信模型將會很熟悉。使用集線器還允許您將強(qiáng)類型參數(shù)傳遞給方法,從而啟用模型綁定。

SignalR將整個(gè)信息的交換封裝起來,客戶端和服務(wù)器都是使用JSON來溝通的,在服務(wù)端聲明的所有Hub信息,都會生成JavaScript輸出到客戶端,.NET則依賴Proxy來生成代理對象,而Proxy的內(nèi)部則是將JSON轉(zhuǎn)換成對象。

參考

官網(wǎng)及學(xué)習(xí)文檔:https://www.asp.net/signalr
github:https://github.com/SignalR
SignalR 參考項(xiàng)目:https://github.com/SignalR/Samples

實(shí)戰(zhàn)

工具要求:

  • Visual Studio 2013 及以上
  • .NET 4.5及以上
  • MVC 5及以上
  • SignalR版本2及以上
  1. 使用Visual Studio 2013,創(chuàng)建一個(gè)MVC項(xiàng)目

  2. 通過Nuget安裝SignalR包。
    install-package Microsoft.AspNet.SignalR

  3. 安裝SignalR成功后,SignalR庫的腳本將被添加進(jìn)Scripts文件夾下。具體如下圖所示:


    image.png
  4. 在解決方案資源管理器中,右鍵單擊該項(xiàng)目(也可以新建一個(gè)類庫),選擇添加 新建文件夾,并添加一個(gè)名為Hubs的新文件夾。
    用鼠標(biāo)右鍵單擊該Hubs文件夾,新建一個(gè)SignalR Hub Class(v2)類,并創(chuàng)建一個(gè)名為Chat .cs。您將使用此類作為將消息發(fā)送到所有客戶端的SignalR服務(wù)器中心。


    image.png
  1. 用下面的代碼替換Chat 類中的代碼。
    public class Chat : Hub
    {
        public void Send(string message)
        {
            Clients.All.send(message);
        }
    }
  1. 創(chuàng)建一個(gè)Startup類,如果開始創(chuàng)建MVC項(xiàng)目的時(shí)候沒有更改身份驗(yàn)證的話,這個(gè)類會默認(rèn)添加的,如果已有就不需要重復(fù)添加了。按照如下代碼更新Startup類。
using Owin;
using Microsoft.Owin;
[assembly: OwinStartup(typeof(SignalRChat.Startup))]
namespace SignalRChat
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Any connection or hub wire up and configuration should go here
            app.MapSignalR();
        }
    }
}
  1. 編輯HomeController在Controllers / HomeController.cs中找到的類,并將以下方法添加到類中。此方法返回您將在稍后的步驟中創(chuàng)建的聊天視圖。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace BasicChat.Mvc.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your app description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}

  1. 用以下代碼替換Chat.cshtml的內(nèi)容。

將SignalR和其他腳本庫添加到Visual Studio項(xiàng)目中時(shí),程序包管理器可能會安裝比本主題中顯示的版本更新的SignalR腳本文件版本。確保代碼中的腳本引用與項(xiàng)目中安裝的腳本庫的版本相匹配。

@{
    ViewBag.Title = "聊天窗口";
}

<h2>Chat</h2>

<div class="container">
    <input type="text" id="message" />
    <input type="button" id="sendmessage" value="Send" />
    <input type="hidden" id="displayname" />
    <ul id="discussion"></ul>
</div>

@section scripts
{
    <!--引用SignalR庫. -->
    <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
     <!--引用自動(dòng)生成的SignalR 集線器(Hub)腳本.在運(yùn)行的時(shí)候在瀏覽器的Source下可看到 -->
    <script src="~/signalr/hubs"></script>
    
    <script>
        $(function () {
            // 引用自動(dòng)生成的集線器代理
            var chat = $.connection.serverHub;
            // 定義服務(wù)器端調(diào)用的客戶端sendMessage來顯示新消息
           
            chat.client.sendMessage = function (name, message) {
                // 向頁面添加消息
                $('#discussion').append('<li><strong>' + htmlEncode(name)
                    + '</strong>: ' + htmlEncode(message) + '</li>');
            };
           
            // 設(shè)置焦點(diǎn)到輸入框
            $('#message').focus();
            // 開始連接服務(wù)器
            $.connection.hub.start().done(function () {
                $('#sendmessage').click(function () {
                    // 調(diào)用服務(wù)器端集線器的Send方法
                    chat.server.send($('#message').val());
                    // 清空輸入框信息并獲取焦點(diǎn)
                    $('#message').val('').focus();
                });
            });
        });
        
        // 為顯示的消息進(jìn)行Html編碼
        function htmlEncode(value) {
            var encodedValue = $('<div />').text(value).html();
            return encodedValue;
        }
    </script>
    }

效果

image.png

SignalR聊天應(yīng)用程序演示了兩個(gè)基本的SignalR開發(fā)任務(wù):創(chuàng)建一個(gè)集線器作為服務(wù)器上的主要協(xié)調(diào)對象,并使用SignalR jQuery庫來發(fā)送和接收消息。

在代碼示例中,ChatHub類從Microsoft.AspNet.SignalR.Hub類派生。從Hub類派生是構(gòu)建SignalR應(yīng)用程序的有效方法。您可以在Hub類上創(chuàng)建公共方法,然后通過從網(wǎng)頁中的腳本調(diào)用這些方法來訪問這些方法。

在聊天代碼中,客戶端調(diào)用ChatHub.Send方法發(fā)送一條新消息。集線器通過調(diào)用Clients.All.addNewMessageToPage將消息發(fā)送給所有客戶端。
所述發(fā)送方法演示幾個(gè)集線器概念:

  • 在集線器上聲明公用方法,以便客戶端可以調(diào)用它們。
  • 使用Microsoft.AspNet.SignalR.Hub.Clients屬性訪問連接到此集線器的所有客戶端。
  • 調(diào)用客戶端上的函數(shù)(如addNewMessageToPage函數(shù))來更新客戶端。
    SignalR和jQuery

代碼示例中的Chat.cshtml視圖文件顯示了如何使用SignalR jQuery庫與SignalR集線器進(jìn)行通信。代碼中的基本任務(wù)是創(chuàng)建對集線器的自動(dòng)生成代理的引用,聲明服務(wù)器可以調(diào)用的將內(nèi)容推送到客戶端的功能,以及啟動(dòng)連接以將消息發(fā)送到集線器。

以下代碼顯示了如何在腳本中創(chuàng)建回調(diào)函數(shù)。服務(wù)器上的集線器類調(diào)用此函數(shù)將內(nèi)容更新推送到每個(gè)客戶端。對htmlEncode函數(shù)的可選調(diào)用顯示了一種在將消息內(nèi)容顯示在頁面之前對其進(jìn)行HTML編碼的方法,以防止腳本注入。

chat.client.addNewMessageToPage = function (name, message) {
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + htmlEncode(name) 
        + '</strong>: ' + htmlEncode(message) + '</li>');
};

以下代碼顯示如何打開與集線器的連接。代碼啟動(dòng)連接,然后傳遞一個(gè)函數(shù)來處理“聊天”頁面中“ 發(fā)送”按鈕上的點(diǎn)擊事件。

這種方法可以確保在事件處理程序執(zhí)行之前建立連接。

$.connection.hub.start().done(function () {
    $('#sendmessage').click(function () {
        // Call the Send method on the hub. 
        chat.server.send($('#displayname').val(), $('#message').val());
        // Clear text box and reset focus for next comment. 
        $('#message').val('').focus();
    });
});

按照B/S模式來看,運(yùn)行程序的時(shí)候,Web頁面就與SignalR的服務(wù)建立了連接,具體的建立連接的代碼就是:$.connection.hub.start()。這句代碼的作用就是與SignalR服務(wù)建立連接,后面的done函數(shù)表明建立連接成功后為發(fā)送按鈕注冊了一個(gè)click事件,當(dāng)客戶端輸入內(nèi)容點(diǎn)擊發(fā)送按鈕后,該Click事件將會觸發(fā),觸發(fā)執(zhí)行的操作為: chat.server.send($('#message').val())。這句代碼表示調(diào)用服務(wù)端的send函數(shù),而服務(wù)端的Send韓式又是調(diào)用所有客戶端的sendMessage函數(shù),而客戶端中sendMessage函數(shù)就是將信息添加到對應(yīng)的消息列表中。這樣就實(shí)現(xiàn)了廣播消息的功能了。

ABP實(shí)時(shí)服務(wù) - 集成SignalR

簡介

在基于ABP創(chuàng)建的項(xiàng)目中,有一個(gè)很容易的方式使用 SignalR,那就是使用Abp.Web.SignalR。詳情請參考SignalR文檔

安裝

使用Nuget安裝[Abp.Web.SignalR(http://www.nuget.org/packages/Abp.Web.SignalR)到你的項(xiàng)目中(通常是你的Web項(xiàng)目)并且在模塊中添加被依賴的模塊:

[DependsOn(typeof(AbpWebSignalRModule))]
public class YourProjectWebModule : AbpModule
{
    //...
}

然后,在你的OWIN Startup類中使用 MapSignalR 方法,正如你往常那樣做的:

[assembly: OwinStartup(typeof(Startup))]
namespace MyProject.Web
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();

            //...
        }
    }
}

注意:Abp.Web.SignalR 依賴于
Microsoft.AspNet.SignalR.Core。所以,你需要安裝 Microsoft.AspNet.SignalR到你的Web項(xiàng)目中。詳情請參考SignalR文檔。

客戶端

腳本 abp.signalr.js 應(yīng)該被引用到頁面中。它位于 Abp.Web.Resources 包中(它已經(jīng)被安裝到啟動(dòng)模板中)。 我們應(yīng)該在signalr hubs 后引用它:

<script src="~/signalr/hubs"></script>
<script src="~/Abp/Framework/scripts/libs/abp.signalr.js"></script>

這么做了以后,SignalR就已經(jīng)恰當(dāng)?shù)呐渲煤图傻侥愕捻?xiàng)目中了。

建立連接

當(dāng) abp.signalr.js 被引用到頁面后,ABP會自動(dòng)的連接到你的服務(wù)器。一般我們都會這么做,但是在某些情況下你不想這樣做。你可以像下面代碼所示禁用自動(dòng)連接:

<script>
    abp.signalr = abp.signalr || {};
    abp.signalr.autoConnect = false;
</script>

在這種情況下,你可以手動(dòng)調(diào)用 abp.signalr.connect() 函數(shù)來連接服務(wù)器。

當(dāng)客戶端連接到服務(wù)器時(shí),全局事件 "abp.signalr.connected" 會被觸發(fā)。當(dāng)連接建立成功的時(shí)候,你可以注冊這個(gè)事件來采取相應(yīng)的行動(dòng)。詳情請參考Javascript函數(shù)庫

內(nèi)置功能

你可以在應(yīng)用程序中使用所有的SignalR的功能。還有,在 Abp.Web.SignalR 中實(shí)現(xiàn)了一些內(nèi)置功能。

1. 通知

Abp.Web.SignalR 實(shí)現(xiàn)了 IRealTimeNotifier 接口來發(fā)送實(shí)時(shí)時(shí)間到客戶端。因此,你的用戶可以獲得實(shí)時(shí)的推送通知。

2. 在線客戶端

ABP提供了 IOnlineClientManager 來取得在線用戶的信息(如:注入IOnlineClientManager以及使用GetByUserIdOrNull, GetAllClients, IsOnline 方法)。為了能夠正常工作,IOnlineClientManager需要一個(gè)通信基礎(chǔ)設(shè)施。Abp.Web.SignalR 提供了這個(gè)基礎(chǔ)設(shè)施。如果安裝了SignalR,那么在應(yīng)用的任何層都可以注入并使用IOnlineClientManager。

3. PascalCase vs. camelCase

Abp.Web.SignalR 使用 CamelCasePropertyNamesContractResolver 重寫了 SignalR's 默認(rèn)的序列化類 ContractResolver。因此,在服務(wù)器端類具有 PascalCase 屬性,而在客戶端為了發(fā)送/接受對象,我們使用 camelCase (因?yàn)閏amelCase在JavaScript中更受歡迎)。如果你想在某些程序集中忽略這個(gè),那么你可以將那些程序集添加AbpSignalRContractResolver.IgnoredAssemblies 列表中。

你的SignaR代碼

使用 Abp.Web.SignalR 包也會簡化你的 SignalR代碼。假設(shè)我們想要添加一個(gè)Hub到你的應(yīng)用程序中:

public class MyChatHub : Hub, ITransientDependency
{
    public IAbpSession AbpSession { get; set; }

    public ILogger Logger { get; set; }

    public MyChatHub()
    {
        AbpSession = NullAbpSession.Instance;
        Logger = NullLogger.Instance;
    }

    public void SendMessage(string message)
    {
        Clients.All.getMessage(string.Format("User {0}: {1}", AbpSession.UserId, message));
    }

    public async override Task OnConnected()
    {
        await base.OnConnected();
        Logger.Debug("A client connected to MyChatHub: " + Context.ConnectionId);
    }

    public async override Task OnDisconnected(bool stopCalled)
    {
        await base.OnDisconnected(stopCalled);
        Logger.Debug("A client disconnected from MyChatHub: " + Context.ConnectionId);
    }
}

為了使我們的Hub可以簡單的注冊到依賴注入系統(tǒng)中,我們可以實(shí)現(xiàn) ITransientDependency 接口。當(dāng)然你可以根據(jù)你的需求,注冊它為單例模式。我們也使用屬性注入了SessionLogger

SendMessage是hub的一個(gè)方法,它可以被客戶端使用。在這個(gè)方法中,我們可以調(diào)用所有客戶端的 getMessage函數(shù)。正如你看到的那樣,我們可以使用AbpSession來獲得當(dāng)前的用戶id(如果用戶已經(jīng)登錄)。為了演示,我們也重寫了 OnConnected 和 OnDisconnected,實(shí)際這里是不需要的。

下面是用在Hub中,用來發(fā)送/接受信息的客戶端腳本:

var chatHub = $.connection.myChatHub; //get a reference to the hub

chatHub.client.getMessage = function (message) { //register for incoming messages
    console.log('received message: ' + message);
};

abp.event.on('abp.signalr.connected', function() { //register for connect event
    chatHub.server.sendMessage("Hi everybody, I'm connected to the chat!"); //send a message to the server
});

然后,在我們需要發(fā)送信息到服務(wù)器時(shí),我們就可以使用 chatHub。詳情請參考SignalR文檔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容