ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十六节--SignalR与ABP框架Abp.Web.SignalR及扩展

SignalR简介

SignalR是什么?

ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程。实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据。

ASP.NET SignalR是ASP.NET开发人员的一个新库,可以使开发实时Web功能变得简单。SignalR允许服务器和客户端之间的双向通信。服务器现在可以将内容即时推送到连接的客户端。SignalR包含用于连接管理(例如连接和断开事件),连接分组和授权的API。一般情况下,SignalR会使用JavaScript(Ajax长时间轮询)的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信和支持CORS(跨源资源共享)。另外SignalR开发的程序不仅仅限制于宿主在IIS中,也可以宿主在任何应用程序,包括控制台,客户端程序和Windows服务等,另外还支持Mono,这意味着它可以实现跨平台部署在Linux环境下。JSONP没有配置,并且连接不是跨域的,如果客户端和服务器都支持,则使用WebSocket。

SignalR内部有两类对象:
Http持久连接(Persisten Connection)对象:

  • Connection表示用于发送单收件人,分组或广播消息的简单终端。持久连接API(由PersistentConnection类的.NET代码表示)为开发人员提供了对SignalR公开的低级通信协议的直接访问。用来解决长时间连接的功能。还可以由客户端主动向服务器要求数据,而服务器端不需要实现太多细节,只需要处理PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。

  • Hub(集线器)对象:基于连接API构建的更高级别的管道,允许您的客户端和服务器直接调用彼此的方法。SignalR像魔术一样处理跨机器边界的调度,允许客户端像本地方法一样方便地调用服务器上的方法,反之亦然。对于使用远程调用API(如.NET Remoting)的开发人员来说,使用Hubs通信模型将会很熟悉。使用集线器还允许您将强类型参数传递给方法,从而启用模型绑定。

SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。

参考

官网及学习文档:https://www.asp.net/signalr
github:https://github.com/SignalR
SignalR 参考项目:https://github.com/SignalR/Samples

实战

工具要求:

  • Visual Studio 2013 及以上
  • .NET 4.5及以上
  • MVC 5及以上
  • SignalR版本2及以上
  1. 使用Visual Studio 2013,创建一个MVC项目

  2. 通过Nuget安装SignalR包。
    install-package Microsoft.AspNet.SignalR

  3. 安装SignalR成功后,SignalR库的脚本将被添加进Scripts文件夹下。具体如下图所示:


    image.png
  4. 在解决方案资源管理器中,右键单击该项目(也可以新建一个类库),选择添加 新建文件夹,并添加一个名为Hubs的新文件夹。
    用鼠标右键单击该Hubs文件夹,新建一个SignalR Hub Class(v2)类,并创建一个名为Chat .cs。您将使用此类作为将消息发送到所有客户端的SignalR服务器中心。


    image.png
  1. 用下面的代码替换Chat 类中的代码。
    public class Chat : Hub
    {
        public void Send(string message)
        {
            Clients.All.send(message);
        }
    }
  1. 创建一个Startup类,如果开始创建MVC项目的时候没有更改身份验证的话,这个类会默认添加的,如果已有就不需要重复添加了。按照如下代码更新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中找到的类,并将以下方法添加到类中。此方法返回您将在稍后的步骤中创建的聊天视图。
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的内容。

将SignalR和其他脚本库添加到Visual Studio项目中时,程序包管理器可能会安装比本主题中显示的版本更新的SignalR脚本文件版本。确保代码中的脚本引用与项目中安装的脚本库的版本相匹配。

@{
    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>
     <!--引用自动生成的SignalR 集线器(Hub)脚本.在运行的时候在浏览器的Source下可看到 -->
    <script src="~/signalr/hubs"></script>
    
    <script>
        $(function () {
            // 引用自动生成的集线器代理
            var chat = $.connection.serverHub;
            // 定义服务器端调用的客户端sendMessage来显示新消息
           
            chat.client.sendMessage = function (name, message) {
                // 向页面添加消息
                $('#discussion').append('<li><strong>' + htmlEncode(name)
                    + '</strong>: ' + htmlEncode(message) + '</li>');
            };
           
            // 设置焦点到输入框
            $('#message').focus();
            // 开始连接服务器
            $.connection.hub.start().done(function () {
                $('#sendmessage').click(function () {
                    // 调用服务器端集线器的Send方法
                    chat.server.send($('#message').val());
                    // 清空输入框信息并获取焦点
                    $('#message').val('').focus();
                });
            });
        });
        
        // 为显示的消息进行Html编码
        function htmlEncode(value) {
            var encodedValue = $('<div />').text(value).html();
            return encodedValue;
        }
    </script>
    }

效果

image.png

SignalR聊天应用程序演示了两个基本的SignalR开发任务:创建一个集线器作为服务器上的主要协调对象,并使用SignalR jQuery库来发送和接收消息。

在代码示例中,ChatHub类从Microsoft.AspNet.SignalR.Hub类派生。从Hub类派生是构建SignalR应用程序的有效方法。您可以在Hub类上创建公共方法,然后通过从网页中的脚本调用这些方法来访问这些方法。

在聊天代码中,客户端调用ChatHub.Send方法发送一条新消息。集线器通过调用Clients.All.addNewMessageToPage将消息发送给所有客户端。
所述发送方法演示几个集线器概念:

  • 在集线器上声明公用方法,以便客户端可以调用它们。
  • 使用Microsoft.AspNet.SignalR.Hub.Clients属性访问连接到此集线器的所有客户端。
  • 调用客户端上的函数(如addNewMessageToPage函数)来更新客户端。
    SignalR和jQuery

代码示例中的Chat.cshtml视图文件显示了如何使用SignalR jQuery库与SignalR集线器进行通信。代码中的基本任务是创建对集线器的自动生成代理的引用,声明服务器可以调用的将内容推送到客户端的功能,以及启动连接以将消息发送到集线器。

以下代码显示了如何在脚本中创建回调函数。服务器上的集线器类调用此函数将内容更新推送到每个客户端。对htmlEncode函数的可选调用显示了一种在将消息内容显示在页面之前对其进行HTML编码的方法,以防止脚本注入。

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

以下代码显示如何打开与集线器的连接。代码启动连接,然后传递一个函数来处理“聊天”页面中“ 发送”按钮上的点击事件。

这种方法可以确保在事件处理程序执行之前建立连接。

$.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模式来看,运行程序的时候,Web页面就与SignalR的服务建立了连接,具体的建立连接的代码就是:$.connection.hub.start()。这句代码的作用就是与SignalR服务建立连接,后面的done函数表明建立连接成功后为发送按钮注册了一个click事件,当客户端输入内容点击发送按钮后,该Click事件将会触发,触发执行的操作为: chat.server.send($('#message').val())。这句代码表示调用服务端的send函数,而服务端的Send韩式又是调用所有客户端的sendMessage函数,而客户端中sendMessage函数就是将信息添加到对应的消息列表中。这样就实现了广播消息的功能了。

ABP实时服务 - 集成SignalR

简介

在基于ABP创建的项目中,有一个很容易的方式使用 SignalR,那就是使用Abp.Web.SignalR。详情请参考SignalR文档

安装

使用Nuget安装[Abp.Web.SignalR(http://www.nuget.org/packages/Abp.Web.SignalR)到你的项目中(通常是你的Web项目)并且在模块中添加被依赖的模块:

[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项目中。详情请参考SignalR文档

客户端

脚本 abp.signalr.js 应该被引用到页面中。它位于 Abp.Web.Resources 包中(它已经被安装到启动模板中)。 我们应该在signalr hubs 后引用它:

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

这么做了以后,SignalR就已经恰当的配置和集成到你的项目中了。

建立连接

当 abp.signalr.js 被引用到页面后,ABP会自动的连接到你的服务器。一般我们都会这么做,但是在某些情况下你不想这样做。你可以像下面代码所示禁用自动连接:

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

在这种情况下,你可以手动调用 abp.signalr.connect() 函数来连接服务器。

当客户端连接到服务器时,全局事件 "abp.signalr.connected" 会被触发。当连接建立成功的时候,你可以注册这个事件来采取相应的行动。详情请参考Javascript函数库

内置功能

你可以在应用程序中使用所有的SignalR的功能。还有,在 Abp.Web.SignalR 中实现了一些内置功能。

1. 通知

Abp.Web.SignalR 实现了 IRealTimeNotifier 接口来发送实时时间到客户端。因此,你的用户可以获得实时的推送通知。

2. 在线客户端

ABP提供了 IOnlineClientManager 来取得在线用户的信息(如:注入IOnlineClientManager以及使用GetByUserIdOrNull, GetAllClients, IsOnline 方法)。为了能够正常工作,IOnlineClientManager需要一个通信基础设施。Abp.Web.SignalR 提供了这个基础设施。如果安装了SignalR,那么在应用的任何层都可以注入并使用IOnlineClientManager。

3. PascalCase vs. camelCase

Abp.Web.SignalR 使用 CamelCasePropertyNamesContractResolver 重写了 SignalR's 默认的序列化类 ContractResolver。因此,在服务器端类具有 PascalCase 属性,而在客户端为了发送/接受对象,我们使用 camelCase (因为camelCase在JavaScript中更受欢迎)。如果你想在某些程序集中忽略这个,那么你可以将那些程序集添加AbpSignalRContractResolver.IgnoredAssemblies 列表中。

你的SignaR代码

使用 Abp.Web.SignalR 包也会简化你的 SignalR代码。假设我们想要添加一个Hub到你的应用程序中:

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可以简单的注册到依赖注入系统中,我们可以实现 ITransientDependency 接口。当然你可以根据你的需求,注册它为单例模式。我们也使用属性注入了SessionLogger

SendMessage是hub的一个方法,它可以被客户端使用。在这个方法中,我们可以调用所有客户端的 getMessage函数。正如你看到的那样,我们可以使用AbpSession来获得当前的用户id(如果用户已经登录)。为了演示,我们也重写了 OnConnected 和 OnDisconnected,实际这里是不需要的。

下面是用在Hub中,用来发送/接受信息的客户端脚本:

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
});

然后,在我们需要发送信息到服务器时,我们就可以使用 chatHub。详情请参考SignalR文档

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容