AngularJS+ASP.NET MVC+SignalR实现消息推送

背景

OA管理系统中,员工提交申请单,消息实时通知到相关人员及时进行审批,审批之后将结果推送给用户。

技术选择

最开始发现的是firebase,于是很兴奋的开始倒腾起来。firebase用 起来倒是简单:引用一个js即可,按官网上的教程很快便应用到了项目中。第二天打开项目发现推送功能不好使了,这是为何?最后发现firebase官网打 不开了。。。难道firebase被google收了也会被天朝给墙掉?也许是firebase自己挂掉了,总之是用不了了。因为要完全把推送数据存放在 firebase服务器上来实现推送功能,就会有以下几个需要担心的问题:

1.数据不安全

2.对firebase依赖性太强

3.firebase收费(免费版太弱)

于是果断放弃firebase,朋友推荐有个叫SignalR的东东可以试试,这是专门为ASP.NET开发人员准备的一款消息推送类库,且不依赖别的服务器、免费。

使用方法

1.在nuget中搜索并添加SignalR最新版本

AngularJS+ASP.NET MVC+SignalR实现消息推送

2.在页面中引用jquery.signalR-2.2.0.min.js文件(依赖jquery),再添加<script src="/signalr/hubs"></script>用于自动生成signalr的脚本

3.添加MsgHub.cs类,用于处理对应用户信息和消息推送实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
 
namespace ZmeiOA.Services
{
          [Authorize]
     [HubName( "ZmHub" )]
     public class MsgHub : Hub
     { /// <summary>
         /// 连接
         /// </summary>
         /// <returns></returns>
         public override System.Threading.Tasks.Task OnConnected()
         {
             Groups.Add(Context.ConnectionId, Context.User.Identity.Name);
             return base .OnConnected();
         }
 
         /// <summary>
         /// 重新连接
         /// </summary>
         /// <returns></returns>
         public override System.Threading.Tasks.Task OnReconnected()
         {
             Groups.Add(Context.ConnectionId, Context.User.Identity.Name);
             return base .OnReconnected();
         }
 
         /// <summary>
         /// 断开连接
         /// </summary>
         /// <param name="stopCalled"></param>
         /// <returns></returns>
         public override System.Threading.Tasks.Task OnDisconnected( bool stopCalled)
         {
             Groups.Remove(Context.ConnectionId, Context.User.Identity.Name);
             return base .OnDisconnected(stopCalled);
         }
     }
}

说明:SignalR的推送消息是基于用户连接(ConnectionId)的,SignalR会为每个会话自动生成一个 ConnectionId。但是我们的推送是基于用户的(权限体系),也就是只有登录之后才注册到此Hub。在这里我用到的是SignalR中的 Groups,把登录用户的ConnectionId与对应的UserId添加到Groups中,推送的时候只要指定Groups的 Name,SignalR便会自动找出其对应的ConnectionId并发送消息(这种方式可能不是最好的,因为每个用户的UserId都会作为 Groups的Key添加进去,当用户量很大的时候Groups也会很庞大,但我还没找到更好的替代方案)。

4.消息推送有两种形式:a.服务端直接推送;b.客户端推送。

区别在于,服务端推送是在持久化数据之后便可以直接把消息推送给相关人;而客户端推送是持久化数据之后,客户端根据返回值的情况,使用 SignalR的JS方法调用服务端的推送功能。我使用的是服务器端直接推送数据,因为在持久化数据之后就完全可以根据业务通知相关人,如果返回到前台之 后再调用服务端的推送方法只是多此一举。

如:保存申请单成功之后立刻通知审批人

在Service中获取Hub的上下文

?
1
2
3
4
         /// <summary>
         /// 消息推送上下文
         /// </summary>
         protected static IHubContext ZmHubContext = GlobalHost.ConnectionManager.GetHubContext<MsgHub>();

在保存申请单之后给相关人员推送消息(注意:dynamic方法broadcastTodo就是在客户端需要接收消息的方法)

?
1
2
3
4
5
6
7
8
public static ApplyForm Save(FormView view)
{        
          //省略业务操作...
          //...
     //通知待办事项
     ZmHubContext.Clients.Groups(app.AuditorIds.Split( ',' )).broadcastTodo(app.AuditorIds,  new { type =  "new" , data = app });
     return app;
}


5.在注册Angular模块时连接Hub并作为value传入到模块中,这样每个controller都可以使用此连接:

?
1
2
3
         var zmHub = $.connection.ZmHub;
 
         var zmApp = angular.module( 'zmApp' , [ 'ngRoute' 'ngResource' 'ngSanitize' 'ngMessages' 'ngSVGAttributes' ]).value( 'zmHub' , zmHub);

  6.在首页的controller中接收推送的消息,并提供两种推送体验:a.桌面通知;b.页面内消息。桌面通知很酷,即使是浏览器最小化的时候,在桌面右下角也可以收到提示(Chrome和Firefox支持,IE不支持)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
zmHub.client.broadcastTodo = function (userIds, obj) {
     //通知下级控制器有待办事项
     $scope.$broadcast( 'todoschanged' , obj);
     //显示桌面通知
     if (obj.type ==  'new' ) {
                  //桌面通知标题
         var title =  '来自[' + obj.data.ApplicantName +  ']的申请单' ;
         //申请单类型名称
         var formTypeName = DefaultService.getEnumText(17, obj.data.Type);
         var msg =  '[' + formTypeName +  ']' + obj.data.Name;
                  //桌面通知方法
         NotifyService.Notify( 'todos' , title, msg);
     }
}

 

下级控制器的接收方法(关于angularjs的broadcast不多解释,不明白的可以到官网查阅):

 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//接收推送的待办事项
$scope.$on( 'todoschanged' , function (d, obj) {
     $scope.$apply(function () {
                  //如果是新增数据,在当前列表中添加一条
         if (obj.type ==  'new' ) {
             $scope.todoApps.unshift(obj.data);
         }
         else if (obj.type ==  'delete' ) { //如果是撤销申请,则把当前列表中那条数据删除
             for (var j = 0; j < $scope.todoApps.length; j++) {
                 if ($scope.todoApps[j].Id == obj.data.Id) {
                     $scope.todoApps.splice(j, 1);
                     break ;
                 }
             }
         }
     });
});

桌面通知服务:

 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//桌面通知服务
zmApp.factory( 'NotifyService' , function () {
 
     return {
         Notify: function (icon, title, msg) {
             // At first, let's check if we have permission for notification
             // If not, let's ask for it
             if (window.Notification && Notification.permission !==  "granted" ) {
                 Notification.requestPermission(function (status) {
                     if (Notification.permission !== status) {
                         Notification.permission = status;
                     }
                 });
             }
             var iconPath =  '/Content/images/icons/' + (icon ||  'info' ) +  '.png' ;
             var options = {
                 lang:  'zh-CN' ,
                 body: msg,
                 icon: iconPath
             };
             var notify;
             // If the user agreed to get notified
             if (window.Notification && Notification.permission ===  "granted" ) {
                 notify =  new Notification(title, options);
             }
             else if (window.Notification && Notification.permission !==  "denied" ) {
                 Notification.requestPermission(function (status) {
                     if (Notification.permission !== status) {
                         Notification.permission = status;
                     }
                     if (status ===  "granted" ) {
                         notify =  new Notification(title, options);
                     }
                     else {
                         console.log( '您禁止了桌面通知,无法推送到您的桌面!' );
                     }
                 });
             }
             else {
                 console.log( '您禁止了桌面通知,无法推送到您的桌面!' );
             }
             if (notify) {
                 notify.onclose = function (evt) {
 
                 };
                 //点击切换到浏览器
                 notify.onclick = function () {
                     window.focus();
                 };
             }
         }
     };
});

桌面通知效果:

AngularJS+ASP.NET MVC+SignalR实现消息推送

总结:

用SignalR推送消息总的来说比较简单,只需要简单几步便可实现,而且是selfhost,不必担心对其它服务器的依赖和数据安全问题,感兴趣的朋友可以试试

来自:http://www.cnblogs.com/usea/p/4282560.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值