如何在ASP.NET Core中使用SignalR构建与Angular通信的实时通信应用程序


图片

假设我们要创建一个监视Web应用程序,该应用程序为用户提供了一个能够显示一系列信息的仪表板,这些信息会随着时间的推移而更新。

第一种方法是在定义的时间间隔(轮询)定期调用API 以更新仪表板上的数据。

无论如何,还是有一个问题:如果没有更新的数据,我们会因请求而不必要地增加网络流量。

一种替代方法是长轮询技术:如果服务器没有可用数据,则它可以使请求保持活动状态,直到发生某种情况或达到预设的超时时间为止,而不是发送空响应。如果存在新数据,则完整的响应将到达客户端。完全不同的方法是反转角色:当有新数据可用(推送)时,后端与客户端联系。

请记住,HTML 5具有标准化的WebSocket,这是一个永久的双向连接,可以在兼容的浏览器中使用Javascript接口进行配置。不幸的是,必须在客户端和服务器端都对WebSocket提供完全支持,以使其可用。然后,我们需要提供替代系统(fallback),无论如何,该替代系统都允许我们的应用程序运行。

微软于2013年发布了一个名为SignalR for ASP.NET的开源库,该库已于 2018年为ASP.NET Core进行了重写。SignalR从与通信机制有关的所有细节中进行抽象,并从可用的信息中选择最佳的一种。

结果是有可能编写代码,就像我们一直处于push-mode一样。使用SignalR,服务器可以在其所有连接的客户端或特定客户端上调用JavaScript方法。

我们使用web-api模板创建一个ASP.NET Core项目,删除已生成的示例控制器。使用NuGet,我们将Microsoft.AspNet.SignalR添加到项目中,以创建Hub

集线器是能够调用客户端代码,发送包含所请求方法的名称和参数的消息的高级管道。作为参数发送的对象将使用适当的协议反序列化。客户端在页面代码中搜索与名称相对应的方法,如果找到该名称,则将其调用并传递反序列化的数据作为参数。

using Microsoft.AspNetCore.SignalR;namespace SignalR.Hubs
{public class NotificationHub : Hub { }
}

您可能知道,在ASP.NET Core中,可以配置HTTP请求的管理管道,以添加一些中间件,该中间件可拦截请求,添加已配置的功能并使其进入下一个中间件。必须预先配置SignalR中间件,在Startup 类的ConfigureServices

方法中添加扩展方法services.AddSignalR()。现在,我们可以使用Startup类的

Configure方法中的扩展方法app.UseSignalR()将中间件添加到管道中。

在此操作期间,我们可以传递配置参数,包括集线器的路由:

app.UseSignalR(route =>
{route.MapHub<notificationhub>("/notificationHub");
})

一个有趣的场景允许我们查看ASP.NET Core中的另一个有趣功能,即在后台工作进程上下文中托管SignalR Hub 。 假设我们要实现以下用例:

•运行业务逻辑

•等一下•决定是停止还是重复该过程。

在ASP.NET Core中,我们可以使用框架提供的IHostedService接口在.NET Core应用程序中在后台实现进程的执行。方法要实现是StartAsync()和StopAsync() 。非常简单:StartAsync调用到主机启动,而StopAsync调用到主机关闭。

然后,我们将一个类DashboardHostedService添加到项目中,该类实现

IHostedService。我们在Startup类的ConfigureServices方法中添加接口注册:

services.AddHostedService<dashboardhostedservice>();

在类构造函数DashboardHostedService中,我们注入IHubContext

访问添加到我们应用程序的集线器。在方法StartAsync中,我们设置了一个计时器,它将每两秒钟运行一次方法DoWork()中包含的代码。此方法发送带有四个随意生成的字符串的消息。

但是它向谁传播呢?在我们的示例中,我们正在将消息发送到所有连接的客户端。但是,SignalR提供了向单个用户或用户组发送消息的机会。在本文中[1],您将找到涉及ASP.NET Core中的身份验证和授权功能的详细信息。

有趣的是,用户可以同时在台式机和移动设备上连接。每个设备都有一个单独的SignalR连接,但是它们都将与同一用户关联。

using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Hosting;
using SignalR.Hubs;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;namespace SignalR
{public class DashboardHostedService: IHostedService{private Timer _timer;private readonly IHubContext<notificationhub> _hubContext;public DashboardHostedService(IHubContext<notificationhub> hubContext){_hubContext = hubContext;}public Task StartAsync(CancellationToken cancellationToken){_timer = new Timer(DoWork, null, TimeSpan.Zero,TimeSpan.FromSeconds(2));return Task.CompletedTask;}private void DoWork(object state){_hubContext.Clients.All.SendAsync("SendMessage", new {val1 = getRandomString(),val2 = getRandomString(),val3 = getRandomString(),val4 = getRandomString()});}public Task StopAsync(CancellationToken cancellationToken){_timer?.Change(Timeout.Infinite, 0);return Task.CompletedTask;}}
}

让我们看看如何管理客户端部分。例如,我们使用Angular CLI的ng new SignalR命令创建Angular应用程序。

然后我们安装SignalR的包节点(

npm i @ aspnet / signalr

)。然后添加一个服务,该服务使我们可以连接到先前创建的集线器并接收消息。在这里,第一种可能的方法是,基于服务getMessage()中Observable 的服务,通过使用私有声明的Subject 来返回(Message是与从Object返回的对象相对应的Typescript接口。后端):

@Injectable({providedIn: 'root'
})
export class SignalRService {private message$: Subject<message>;private connection: signalR.HubConnection;constructor() {this.message$ = new Subject<message>();this.connection = new signalR.HubConnectionBuilder().withUrl(environment.hubUrl).build();this.connect();}private connect() {this.connection.start().catch(err => console.log(err));this.connection.on('SendMessage', (message) => {this.message$.next(message);});}public getMessage(): Observable<message> {return this.message$.asObservable();}public disconnect() {this.connection.stop();}
}

在constructor()内部,我们创建一个SignalR.HubConnection类型对象,该对象将用于连接到服务器。我们通过使用文件environment.ts将其传递到其中心URL:

this.connection = new signalR.HubConnectionBuilder().withUrl(environment.hubUrl).build();

构造函数还负责调用connect()方法,该方法进行实际连接,并在控制台中记录可能的错误。

this.connection.start().catch(err => console.log(err));
this.connection.on('SendMessage', (message) => {this.message$.next(message);
});

想要显示来自后端的消息的组件(将其注入到构造函数中的服务),应该订阅getMessage()方法并管理到达的消息。以AppComponent为例,例如:

@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css']
})
export class AppComponent implements OnDestroy {private signalRSubscription: Subscription;public content: Message;constructor(private signalrService: SignalRService) {this.signalRSubscription = this.signalrService.getMessage().subscribe((message) => {this.content = message;});}ngOnDestroy(): void {this.signalrService.disconnect();this.signalRSubscription.unsubscribe();}
}

使用主题允许我们同时管理更多组件,而无论从中心返回的消息(用于订阅还是用于取消订阅)都可以,但是我们必须注意对主题的粗心使用。让我们考虑以下getMessage()版本:

public getMessage(): Observable<message> {return this.message$;
}

现在,该组件也可以使用以下简单代码发送一条消息:

const produceMessage = this.signalrService.getMessage() as Subject<any>;produceMessage.next( {val1: 'a'});
</any>

如果方法getMessage()返回SubjectasObservable,则此代码将引发异常!我们可以在单个组件的情况下使用的第二种方法(更简单)对管理来自后端的消息感兴趣:

@Injectable({providedIn: 'root'
})
export class SignalrService {connection: signalR.HubConnection;constructor() {this.connection = new signalR.HubConnectionBuilder().withUrl(environment.hubAddress).build();this.connect();}public connect() {if (this.connection.state === signalR.HubConnectionState.Disconnected) {this.connection.start().catch(err => console.log(err));}}public getMessage(next) {this.connection.on('SendMessage', (message) => {next(message);});}public disconnect() {this.connection.stop();}
}

我们可以简单地将函数回调传递给方法getMessage,该函数将来自后端的消息作为参数。在这种情况下,AppComponent可以成为:

public content: IMessage;
constructor(private signalrService: SignalrService) {this.signalrService.getMessage((message: IMessage) => {this.content = message;});
}
ngOnDestroy(): void {this.signalrService.disconnect();
}

最后几行代码分别位于app.component.html和app.component.css中

,以赋予一些时尚,并且该应用程序已完成。

<div style="text-align:center"><h1>DASHBOARD</h1>
</div>
<div class="card-container"><div class="card"><div class="container"><h4><b>Valore 1</b></h4><p>{{content.val1}}</p></div></div><div class="card"><div class="container"><h4><b>Valore 2</b></h4><p>{{content.val2}}</p></div></div><div class="card"><div class="container"><h4><b>Valore 3</b></h4><p>{{content.val3}}</p></div></div><div class="card"><div class="container"><h4><b>Valore 4</b></h4><p>{{content.val4}}</p></div></div>
</div>.card-container {display: flex;flex-wrap: wrap;
}.card {box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);transition: 0.3s;width: 40%;flex-grow: 1;margin: 10px;
}.card:hover {box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}.container {padding: 2px 16px;
}

我们首先启动后端,然后启动前端并检查最终结果:

看起来不错!您可以在这里找到代码:https[2] : //github.com/AARNOLD87/SignalRWithAngular[3]

下次见!

References

[1] 本文中: https://docs.microsoft.com/en-us/aspnet/core/signalr/groups?view=aspnetcore-2.2
[2] https: https://github.com/AARNOLD87/SignalRWithAngular
[3] //github.com/AARNOLD87/SignalRWithAngular: https://github.com/AARNOLD87/SignalRWithAngular

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309093.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LED计数电路,5输入按键编码器,7段数码管显示驱动集成为LED计数测试电路

LED计数电路: 5输入按键编码器: 7段数码管显示驱动真值表: 集成:

越卖越涨?腾讯股票3月后大涨45%,超越“阿里”成中国第一,市值相当于14.3个百度!...

01 腾讯股价大涨据股市最新消息&#xff1a;腾讯股价已连续3个交易日上涨, 其中6月22日腾讯股价重返470港元关口&#xff0c;公司市值突破4.5万亿港元&#xff0c;折合4.0万亿人民币&#xff1b;而6月23日上午腾讯股价再度大涨4.05%&#xff0c;刷出493.8港元的新高&#xf…

4位无符号比较器设计

4位比较器原理&#xff1a; 4位比较 a3a2a1a0 : b3b2b1b0&#xff0c;比较顺序从高位到低位&#xff0c;当高位大、小关系确定时则无需看低位&#xff0c;当高位相等时再看相邻低位的关系。 注意&#xff1a;对于三个比较结果&#xff0c;已知其中任意两个&#xff0c;可以用…

关于技术文章“标题党”一事我想说两句

阅读本文大概需要 1.8 分钟。前天发表的一篇文章&#xff0c;标题是&#xff1a;“面试官&#xff1a;你刚说你喜欢研究新技术&#xff0c;那么请说说你对 Blazor 的了解”。确实&#xff0c;这篇文章有标题党的味道&#xff0c;如果因此给部分童鞋带来不适&#xff0c;我在这先…

使用 nuget server 的 API 来实现搜索安装 nuget 包

使用 nuget server 的 API 来实现搜索安装 nuget 包Intronuget 现在几乎是 dotnet 开发不可缺少的一部分了&#xff0c;还没有用过 nuget 的就有点落后时代了&#xff0c;还不快用起来nuget 是 dotnet 里的包管理机制&#xff0c;类似于前端的 npm &#xff0c;php 的 composer…

Kubernetes:通过自动化节省IT预算的4种方法

导语大多数CIO和IT领导者都熟悉反复执行的任务“事半功倍”。在许多组织中&#xff0c;这都是现实&#xff0c;在各种其他因素&#xff08;例如宏观经济状况&#xff09;的影响下&#xff0c;这种趋势往往会起伏不定。正文有时&#xff0c;“用更少的钱做更多的事”可能会更准确…

C# 9.0 终于来了, Top-level programs 和 Partial Methods 两大新特性探究

一&#xff1a;背景1. 讲故事.NET 5 终于在 6月25日 发布了第六个预览版&#xff0c;随之而来的是更多的新特性加入到了 C# 9 Preview 中&#xff0c;这个系列也可以继续往下写了&#xff0c;废话不多说&#xff0c;今天来看一下 Top-level programs 和 Extending Partial Meth…

4位BCD计数器设计

状态转换: 输出函数: 4位BCD计数器设计:

[Mvp.Blazor] 集成Ids4,实现统一授权认证

&#xff08;又一个客户端集成了IdentityServer4&#xff09;还是很开心的&#xff0c;目前已经有六个开源项目都集成到了Ids4认证中心了。1、Blazor系列文章回顾书接上文&#xff0c;关于Blazor学习呢&#xff0c;我也发了几篇文章了&#xff0c;我一般写东西都喜欢偏实战&…

重学ASP.NET Core 中的标记帮助程序

标记帮助程序是什么标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素。 例如&#xff0c;内置的 ImageTagHelper 可以将版本号追加到图片名称。 每当图片发生变化时&#xff0c;服务器都会为图像生成一个新的唯一版本号&#xff0c;因此客户端总能获得…

小型数字系统---运动码表设计

运动码表设计中码表控制器是最难的&#xff0c;所以主要介绍如何设计码表控制器。 我们先给这个时序电路设计状态以及编码 先设计状态转换函数 首先画出状态图: (其中我们让其一到000状态就立马变为001状态&#xff0c;110状态存储完数据立马变成100状态) 根据状态图&#…

用 aforge.net 小试一下验证码识别

今天来小玩一下 aforge.net 套用官方的话就是一个专门为开发者和研究者基于C#框架设计的&#xff0c;这个框架提供了不同的类库和关于类库的资源&#xff0c;还有很多应用程序例子&#xff0c;包括计算机视觉与人工智能&#xff0c;图像处理&#xff0c;神经网络&#xff0c;遗…

国标转区位码电路设计及汉字显示

区位码国际码-2020H&#xff0c;由于采用加法器实现&#xff0c;因此用补码形式进行表示&#xff0c;区位码 国际码FFFF-2020H0001H国际码dfe0。 从网上找下机内码转化的软件&#xff0c;将一段自选文字输入该软件&#xff0c;进行十六进制的机内码转化。由于一个汉字是由四个十…

【WPF】DataGrid多表头的样式设计

需求在使用WPF开发时&#xff0c;使用DataGrid列表显示数据时&#xff0c;有些字段可以进行分组显示&#xff0c;用于更好的表达它们之间存在的某种关系&#xff0c;因此就考虑到要对DataGrid的表头进行扩展&#xff0c;可以显示多行表头&#xff0c;让这些有关联的字段内容显示…