ASP.NET SignalR 是几年前推出的工具,可供 ASP.NET 开发人员使用,以向应用程序添加实时功能。只要基于 ASP.NET 的应用程序必须接收来自服务器(从监视系统到游戏)的频繁异步更新,就属于典型的库用例。这些年来,我还使用它来刷新 CQRS 体系结构方案中的 UI,以及在 socialware 应用程序中实现与 Facebook 类似的通知系统。从更具技术性的角度来看,SignalR 是抽象层,生成依据为一部分可以在完全兼容的客户端和服务器之间建立实时连接的传输机制。客户端通常为 Web 浏览器,服务器通常为 Web 服务器,但两者都不仅限于此。

ASP.NET SignalR 属于 ASP.NET Core 2.1。虽然库的总体编程模型与经典 ASP.NET 的编程模型类似,但库本身实际上已经完全重写。尽管如此,只要开发人员适应各方面的变化,应该就可以快速熟练掌握新方案。本文将介绍如何在规范 Web 应用程序中使用新库来监视可能会很漫长的远程任务。

设置环境

可能需要以下多个 NuGet 包,才能使用库:Microsoft.AspNetCore.SignalR 和 Microsoft.AspNetCore.SignalR.Client。前一个包提供核心功能;后一个包是 .NET 客户端,且只有在生成 .NET 客户端应用程序时才需要。此示例将通过 Web 客户端来使用库,因此改为需要 SignalR NPM 包。本文稍后将详细介绍这一点。请注意,在基于 MVC 应用程序模型的 Web 应用程序的上下文中使用 SignalR 并不是一项强制性要求。可以直接通过 ASP.NET Core 控制台应用程序使用 SignalR 库服务,还可以在控制台应用程序中托管 SignalR 的服务器部分。

应用程序的启动类需要包含一些特定代码,这一点不足为奇。具体而言,将把 SignalR 服务添加到系统服务集合中,并将它配置为可供实际使用。图 1 展示了使用 SignalR 的启动类的典型状态。

图 1:SignalR ASP.NET Core 应用程序的启动类

public class Startup
{public void ConfigureServices(IServiceCollection services){services.AddMvc();services.AddSignalR();}public void Configure(IApplicationBuilder app){app.UseStaticFiles();app.UseDeveloperExceptionPage();// SignalR    app.UseSignalR(routes =>{routes.MapHub<UpdaterHub>("/updaterDemo");routes.MapHub<ProgressHub>("/progressDemo");});app.UseMvcWithDefaultRoute();}
}

SignalR 服务配置包括一个或多个服务器路由的定义,这些路由绑定到服务器端环境中的一个或多个终结点。MapHub<T> 方法将请求 URL 中的指定名称链接到 Hub 类的实例中。Hub 类既是实现 SignalR 协议的核心所在,也是处理客户端调用的位置所在。为服务器端打算接受和处理的每组逻辑相关调用创建 Hub。SignalR 对话由双方之间交换的消息组成,一方对另一方调用方法的结果可能是没有响应,可能是收到一个或多个响应,也可能是仅收到错误通知。任何 ASP.NET Core SignalR 服务器实现都会公开一个或多个 Hub。在图 1 中,有两个 Hub 类(UpdaterHub 和 ProgressHub)绑定到唯一字符串,这些字符串在内部用于生成实际调用的 URL 目标。

Hub 类

SignalR 应用程序中的 Hub 类是普通的简单类,继承自 Hub 基类。此基类仅用于免去开发人员一次又一次编写相同样本代码的麻烦。基类只提供某基础结构,而不提供预定义行为。具体而言,它定义了图 2 中的成员。

图 2:Hub 基类的成员

成员 说明
客户端 公开当前由 Hub 托管的客户端列表的属性。
上下文 公开当前调用方上下文的属性,包括连接 ID 和用户声明(若有)等信息。
公开各客户端子集的属性,这些客户端可能已经以编程方式定义为完整客户端列表中的组。组通常创建用于向选定受众广播特定消息。
OnConnectedAsync 每当有新客户端连接到 Hub 时调用的虚拟方法。
OnDisconnectedAsync 每当有新客户端与 Hub 断开连接时调用的虚拟方法。

最简单的 Hub 类如下所示:

public class ProgressHub : Hub
{
}

有趣的是,如果在 ASP.NET Core MVC 应用程序上下文中从控制器方法内使用它,就会直接采用 Hub 形式。几乎所有的 ASP.NET Core SignalR 示例(包括聊天示例)往往都会在客户端和 Hub 之间进行直接绑定和双向绑定,无需控制器提供任何形式调解。在这种情况下,Hub 采用的形式将会更有形一点:

public class SampleChat : Hub
{     // Invoked from outside the hub  public void Say(string message){// Invoke method on listening client(s)    return Clients.All.InvokeAsync("Said", message);}
}

与数十篇博客文章中换汤不换药的规范 SignalR 聊天示例不同,本文中的示例其实并没有在客户端和服务器之间建立双向对话。虽然连接是从客户端建立的,但在此之后,客户端就不会发送其他任何请求。相反,服务器会监视任务进度,并在适当时将数据推送回客户端。也就是说,只有当用例要求客户端直接调用公共方法时,Hub 类才必须像上面的代码一样使用这些方法。如果有点复杂难懂,下面的示例足以阐明这一点。

监视远程任务

它的具体情形是这样的:ASP.NET Core 应用程序为用户提供了某 HTML 接口,以方便用户触发可能会很漫长的远程任务(如创建报告)。因此,作为开发人员,需要显示进度栏,以持续反馈进度(见图 3)。

图 3:使用 SignalR 监视远程任务的进度

可以猜到,在此示例中,客户端和服务器都在同一个 ASP.NET Core 项目的上下文中设置 SignalR 实时会话。在此开发阶段中,MVC 项目功能齐全,它使用图 1 中的启动代码进行了扩展。接下来,将设置客户端框架。需要在与 SignalR 终结点交互的所有 Razor(或纯 HTML)视图中完成此设置。

若要在 Web 浏览器中与 SignalR 终结点进行通信,首先要添加对 SignalR JavaScript 客户端库的引用:

<script src="~/scripts/signalr.min.js">
</script>

可以通过多种方式获取此 JavaScript 文件。最值得推荐的方法是,使用几乎所有开发计算机上都有的 Node.js 包管理器 (NPM) 工具(特别是在 Visual Studio 2017 版本推出后)。通过 NPM,查找并安装名为 @aspnet/signalr 的 ASP.NET Core SignalR 客户端。它会将许多 JavaScript 文件复制到磁盘,但其中只有一个文件才是大多数情况唯一需要的。不管怎样,这就是简单地链接 JavaScript 文件,还可以通过其他许多方式来获取此文件,包括从旧版 ASP.NET Core SignalR 项目中复制它。然而,NPM 是团队提供的唯一受支持的脚本获取方式。另请注意,ASP.NET Core SignalR 不再依赖 jQuery。

在客户端应用程序中,还需要另一段更具体的 JavaScript 代码。特别是,需要如下代码:

var progressConnection =new signalR.HubConnection("/progressDemo");
progressConnection.start();

与 SignalR Hub 建立的连接与指定路径匹配。更确切地说,以参数形式传递到 HubConnection 的名称,应该是映射到启动类中路由的名称之一。在内部,HubConnection 对象准备了串联当前服务器 URL 和给定名称生成的 URL 字符串。只有当此 URL 与已配置的路由之一匹配时,才会处理它。另请注意,如果客户端和服务器不是相同的 Web 应用程序,那么必须向 HubConnection 传递托管 SignalR Hub 的 ASP.NET Core 应用程序的完整 URL,外加 Hub名称。

然后,必须通过 start 方法打开 JavaScript Hub 连接对象。可以使用 JavaScript 承诺(特别是 then 方法)或 TypeScript 中的 async/await 执行后续操作(如初始化某用户界面)。SignalR 连接由字符串 ID 唯一标识。

请务必注意,如果传输连接或服务器失败,ASP.NET Core SignalR 就不再支持自动重新连接。在旧版中,如果发生服务器故障,客户端会尝试根据计划算法重新建立连接。如果成功,它会使用相同的 ID 重新打开连接。在 SignalR Core 中,如果连接中断,客户端只能通过 start 方法再次启动连接,这就会生成连接 ID 不同的其他连接实例。

客户端回调 API

需要的另一段基本 JavaScript 代码是,Hub 回调的 JavaScript,用于刷新接口并在客户端上反映服务器上的进度。虽然这段代码在 ASP.NET Core SignalR 中的编写方式与旧版中的略有不同,但意向是完全相同的。此示例中有以下三个方法能够从服务器回调:initProgressBar、updateProgressBar 和 clearProgressBar。不用说,可以使用任意名称和签名。以下是实现示例:

progressConnection.on("initProgressBar", () => {setProgress(0);$("#notification").show();
});
progressConnection.on("updateProgressBar", (perc) => {setProgress(perc);
});
progressConnection.on("clearProgressBar", () => {setProgress(100);$("#notification").hide();
});

例如,如果从服务器回调 initProgressBar 方法,帮助程序 setProgress JavaScript 函数就会配置并显示进度栏(此演示使用的是启动进度栏组件)。请注意,代码中使用了 jQuery 库,但仅用于更新 UI。如前所述,客户端 SignalR Core 库不再是 jQuery 插件。也就是说,如果 UI 是基于 Angular 等,可能根本无需使用 jQuery。

服务器端事件

缺少的解决方案部分是,决定何时调用客户端函数。主要有以下两种方案。一种是在客户端通过 Web API 或控制器终结点调用服务器操作时调用。另一种是在客户端直接调用 Hub 时调用。最后,只需决定在哪里为回调客户端的任务编写代码。

在规范聊天示例中,这一切都发生在 Hub 中:执行所需的全部逻辑,并将消息分派给相应连接。监视远程任务是另一回事。它假设后台正在运行某业务流程,以通过某种方式通知进度。从技术角度来讲,可能会在 Hub 中编码此流程,并从中建立与客户端 UI 之间的对话。也可以让控制器 (API) 触发此流程,Hub 仅用于将事件传递给客户端。比此示例更为实际的做法是,在低于控制器级别的层中编码此流程。

总而言之,可以定义 Hub 类,并随时可将它用于决定何时以及是否调用客户端函数。有趣的地方在于,需要什么才能将 Hub 实例注入控制器或其他业务类。此演示在控制器中注入 Hub,但也会对其他更深级别的类执行完全相同的操作。示例 TaskController 是通过 JavaScript 直接从客户端调用,以触发进度栏将显示其进度的远程任务:

public class TaskController : Controller
{private readonly IHubContext<ProgressHub> _progressHubContext;public TaskController(IHubContext<ProgressHub> progressHubContext){_progressHubContext = progressHubContext;}public IActionResult Lengthy(){// Perform the task and call back  }
}

通过 IHubContext<THub> 接口在控制器或其他任何类中注入 Hub。IHubContext 接口封装 Hub 实例,但无法直接访问它。从中可以将消息分派回 UI,但无法访问连接 ID(举个例子)。假设远程任务是在 Lengthy 方法中执行,并需要在其中更新客户端进度栏:

progressHubContext.Clients.Client(connId).InvokeAsync("updateProgressBar", 45);

连接 ID 可以从 Hub 类中进行检索,但无法像此示例一样从通用 Hub 上下文中进行检索。因此,最简单的方法是,让客户端方法在启动远程任务时就传递连接字符串:

public void Lengthy([Bind(Prefix="id")] string connId) { … }

最后,控制器类接收 SignalR 连接 ID,注入有 Hub 上下文,并使用通过非类型化通用 API 调用的上下文方法(InvokeAsync 方法)执行操作。这样一来,Hub 类就无需包含任何方法!如果觉得这很奇怪,请参阅 bit.ly/2DWd8SV 中的代码。

总结

本文介绍了如何在 Web 应用程序上下文中使用 ASP.NET Core SignalR 监视远程任务。Hub 几乎是空的,因为所有通知逻辑都被内置到控制器中,并使用通过 DI 注入的 Hub 上下文进行编排。这只是 ASP.NET Core SignalR 漫长旅程的起点。接下来,我将深入研究基础结构,并探索类型化 Hub。


Dino Esposito 在他 25 年的职业生涯中撰写了超过 20 本书籍和 1,000 篇文章。Esposito 不仅是舞台剧《事业中断》的作者,还是 BaxEnergy 的数字策略分析师,正忙于编写有助于建设环保世界的软件。可以在 Twitter 上关注他 (@despos)。

原文:https://msdn.microsoft.com/zh-cn/magazine/mt846469


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

发现 ASP.NET Core SignalR相关推荐

  1. ASP.NET Core SignalR中的流式传输

    什么是流式传输? 流式传输是这一种以稳定持续流的形式传输数据的技术. 流式传输的使用场景 有些场景中,服务器返回的数据量较大,等待时间较长,客户端不得不等待服务器返回所有数据后,再进行相应的操作.这时 ...

  2. Asp.Net Core SignalR 用泛型Hub优雅的调用前端方法及传参

    继续学习 最近一直在使用Asp.Net Core SignalR(下面成SignalR Core)为小程序提供websocket支持,前端时间也发了一个学习笔记,在使用过程中稍微看了下它的源码,不得不 ...

  3. GitHub高赞!ASP.NET Core SignalR聊天室开源了!

    能双向通信的SignalR框架,很多人都感兴趣却又玩不转,最近发现一个基于.NET6的ASP.NET Core SignalR聊天室,完成了基于SqlSugar+SQLServer登陆退出和聊天存档等 ...

  4. [asp.net core]SignalR一个例子

    摘要 在一个后台管理的页面想实时监控一些操作的数据,想到用signalR. 一个例子 asp.net core+signalR 使用Nuget安装包:Microsoft.AspNetCore.Sign ...

  5. 基于ASP.NET Core SignalR的流式传输

    基于ASP.NET Core SignalR的流式传输 SignalR概述 SignalR是ASP.NET Core下非常流行的实现Web实时功能的库.微软再文档中列出了适合的应用场景: 适合 Sig ...

  6. Asp.Net Core SignalR 与微信小程序交互笔记

    什么是Asp.Net Core SignalR Asp.Net Core SignalR 是微软开发的一套基于Asp.Net Core的与Web进行实时交互的类库,它使我们的应用能够实时的把数据推送给 ...

  7. ASP.NET Core SignalR:集线器Hub

    一.什么是集线器hubs 通过SignalR的集线器hubs中定义的方法,服务器可以调用连接中的客户端定义的方法,而客户端也可以调用服务器端集线器中定义的方法.SignalR负责实现了客户端和服务器之 ...

  8. 在Typescript中使用ASP.NET Core SignalR和React创建实时应用程序

    目录 介绍 ScrumPoker应用程序 源代码 开发工具 基本步骤 后端代码 创建Hub 在Startup中注册集线器 创建持久性 让我们为客户端应用程序公开一些终端 启用Cors 前端代码 结论 ...

  9. Asp.Net Core SignalR获取集线器实例,从集线器外部发送消息

    一.从控制器中获取IHubContext 操作发送消息 此种方式,常用简单 public class HomeController : Controller {private readonly IHu ...

最新文章

  1. 刷题两个月,从入门到字节跳动offer,这是我的模板 | GitHub 1.2k星
  2. 开发日记-20190429 关键词 患病 NDK
  3. python3.6使用教程-如何使用python编程《python3.6 教程》
  4. 使用dokcer搭建个人博客网站
  5. WinCE启动次数的记录
  6. kafka 消息服务
  7. 使用所有对象共有的方法
  8. centos7光盘修复 grub_centos7修复grub2
  9. 织梦后台对应的php文件,织梦DedeCMS后台文件列表按文件名排序的方法
  10. [poj 3436]最大流+输出结果每条边流量
  11. phpstudy 升级mysql 及MySQL服务等问题
  12. CCF201909-1 小明种苹果(100分)【序列处理】
  13. ansible 第四次作业
  14. [转]网络性能评估工具Iperf详解(可测丢包率)
  15. Windows下GDAL3.1.2编译 (VS2015)
  16. linux下飞信安装详细过程
  17. MCGS组态屏CRC16(Modbus)校验计算脚本
  18. php获取客户端ip地址或者服务器ip地址
  19. JavaScript线条变形动画网页js特效
  20. 08_Linux系统之link(),symlink(),readlink()函数

热门文章

  1. 脚本进阶,函数调用实例练习
  2. flex容器属性(一)
  3. OSChina 周六乱弹 —— 有人骂你神经病怎么办?
  4. 把C#.NET程序移植到DB2上的经验浅谈(C#连接DB2可以用IBM.Data.DB2.dll)
  5. 黑马程序员--IO【1】
  6. 模块XX.dll已加载,但对DllRegisterServer的调用失败
  7. Visual Basic、C# 和 C++ 的数据类型比较(转)
  8. 如何在构建docker镜像时执行SonarQube扫描.NET Core应用
  9. 太神奇了!使用C#实现自动核验健康码:(2)OCR识别
  10. 为什么应该用record来定义DTO(续)