图片

假设我们要创建一个监视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

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

  1. 如何在 ASP.Net Core 中使用 SignalR

    SignalR for ASP.Net Core 是 SignalR 的浴火重生版,允许你在 ASP.Net Core 中实现实时通讯,这里的 实时 意味着双方都能快速的感知对方发来的消息,比如:一旦 ...

  2. 如何在 ASP.NET Core 中使用 HttpClientFactory ?

    ASP.Net Core 是一个开源的,跨平台的,轻量级模块化框架,可用它来构建高性能的Web程序,这篇文章我们将会讨论如何在 ASP.Net Core 中使用 HttpClientFactory. ...

  3. 如何在 ASP.Net Core 中使用 Autofac

    依赖注入可以有效的实现对象之间的 松耦合 并能够实现代码的可测试和可维护性,ASP.Net Core 提供了一个极简版的容器实现对 依赖注入 的原生支持,然而内置的依赖注入容器相比成熟的 依赖注入容器 ...

  4. 如何在 ASP.Net Core 中使用 Lamar

    ASP.Net Core 自带了一个极简的 开箱即用 的依赖注入容器,实际上,你还可以使用第三方的 依赖注入容器 来替代它,依赖注入是一种设计模式,它能够有效的实现对象之间的解耦并有利于提高单元测试和 ...

  5. 如何在 ASP.Net Core 中使用 MediatR

    MediatR 是一个 中介者模式 的.NET开源实现, 中介者模式 管控了一组对象之间的相互通讯并有效的减少了对象之间错综复杂的相互依赖,在 中介者模式 中,一个对象不需要直接和另一个对象进行通讯, ...

  6. 如何在 ASP.Net Core 中对接 WCF

    在 REST API 出现之前,SOAP (Simple Object Access Protocol) 一直都是基于 web 的标准协议,虽然现在 REST 大行其道,但在平时开发中总会遇到对接第三 ...

  7. 如何在 ASP.Net Core 中使用 NCache

    虽然 ASP.Net Core 中缺少 Cache 对象,但它引入了三种不同的cache方式. 内存缓存 分布式缓存 Response缓存 Alachisoft 公司提供了一个开源项目 NCache, ...

  8. 如何在 ASP.Net Core 中使用 Configuration Provider

    ASP.NET Core 是一个开源的,跨平台的,精简的模块化框架,可用于构建高性能,可扩展的web应用程序, ASP.NET Core 中的数据配置常用 k-v 的形式存储,值得注意的是,新的数据配 ...

  9. 如何在 ASP.Net Core 中使用 Serilog

    记录日志的一个作用就是方便对应用程序进行跟踪和排错调查,在实际应用上都是引入 日志框架,但如果你的 日志文件 包含非结构化的数据,那么查询起来将是一个噩梦,所以需要在记录日志的时候采用结构化方式. 将 ...

最新文章

  1. Nginx+Apache Yii2.0 配置方案
  2. Spring Boot 2.3.0 新特性:优雅停机!
  3. PostSharp AOP编程:3.PostSharp的LocationInterceptionAspect类基本组成
  4. 2019厦门科技中学计算机特长,中考快讯 | 2019厦门市高中特长生招生学校名单出炉!...
  5. python开发测试岗_作为测试开发岗的面试官,我都是怎么选人的?
  6. 垒骰子|2015年蓝桥杯B组题解析第九题-fishers
  7. JSP/Servlet中的汉字编码问题
  8. 【JAVA 第三章 流程控制语句】课后习题 温度转换
  9. python提取txt字符串,教你一招!Python读取文件内容为字符串的方法
  10. 在 .NET Framework Data Provider for Microsoft SQL Server Compact 3.5 中发生错误
  11. 前端开发人员常用网站
  12. 关于ArrayList和Vector区别
  13. 行军导航过程中导向箭头
  14. setuna截图怎么放大缩小_一款强大的电脑截图神器,快速提高工作效率,功能强大!...
  15. [转载]辐射定标、辐射校正、几何校正的区别
  16. java炒股软件_股票软件java,中国软件股票怎么样
  17. 青花瓷(charles)的基本使用和注意事项
  18. Prescan入门教程之避坑笔记:初学者初用
  19. Wker_SQLTool注入工具(附源码)
  20. 银联二维码支付java 实现

热门文章

  1. 做程序员的纠结在哪里
  2. mongo学习笔记(二):聚合,游标
  3. Java的原始数据类型一共就8个
  4. 读书笔记:季羡林关于如何做研究学问的心得
  5. mysql 中执行的 sql 注意字段之间的反向引号和单引号
  6. DB2 pureScale在线备份恢复实例1
  7. Unity中Quaternion的含义及其使用
  8. JavaScript 学习提升
  9. 数据库时区那些事儿 - MySQL的时区处理
  10. 一个脚本实现全量增量备份,并推送到远端备份中心服务器