8.2 ABP实时服务 - 集成SignalR

8.2.1 简介

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

8.2.2 安装

1. 服务器端

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

  1. [DependsOn(typeof(AbpWebSignalRModule))]
  2. public class YourProjectWebModule : AbpModule
  3. {
  4. //...
  5. }

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

  1. [assembly: OwinStartup(typeof(Startup))]
  2. namespace MyProject.Web
  3. {
  4. public class Startup
  5. {
  6. public void Configuration(IAppBuilder app)
  7. {
  8. app.MapSignalR();
  9. //...
  10. }
  11. }
  12. }

注意:Abp.Web.SignalR 依赖于 Microsoft.AspNet.SignalR.Core。所以,你需要安装 Microsoft.AspNet.SignalR到你的Web项目中。详情请参考SignalR文档

2. 客户端

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

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

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

3. 建立连接

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

  1. <script>
  2. abp.signalr = abp.signalr || {};
  3. abp.signalr.autoConnect = false;
  4. </script>

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

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

8.2.3 内置功能

你可以在应用程序中使用所有的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 列表中。

8.2.3 你的SignaR代码

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

  1. public class MyChatHub : Hub, ITransientDependency
  2. {
  3. public IAbpSession AbpSession { get; set; }
  4. public ILogger Logger { get; set; }
  5. public MyChatHub()
  6. {
  7. AbpSession = NullAbpSession.Instance;
  8. Logger = NullLogger.Instance;
  9. }
  10. public void SendMessage(string message)
  11. {
  12. Clients.All.getMessage(string.Format("User {0}: {1}", AbpSession.UserId, message));
  13. }
  14. public async override Task OnConnected()
  15. {
  16. await base.OnConnected();
  17. Logger.Debug("A client connected to MyChatHub: " + Context.ConnectionId);
  18. }
  19. public async override Task OnDisconnected(bool stopCalled)
  20. {
  21. await base.OnDisconnected(stopCalled);
  22. Logger.Debug("A client disconnected from MyChatHub: " + Context.ConnectionId);
  23. }
  24. }

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

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

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

  1. var chatHub = $.connection.myChatHub; //get a reference to the hub
  2. chatHub.client.getMessage = function (message) { //register for incoming messages
  3. console.log('received message: ' + message);
  4. };
  5. abp.event.on('abp.signalr.connected', function() { //register for connect event
  6. chatHub.server.sendMessage("Hi everybody, I'm connected to the chat!"); //send a message to the server
  7. });

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