构建实时应用程序在现代 Web 开发中变得至关重要,尤其是对于通知、聊天系统和实时更新等功能。SignalR 是一个强大的 ASP.NET 库,支持服务器端代码和客户端 Web 应用程序之间的无缝实时通信。在本指南中,我们将演练如何使用最少的 API 和 SignalR 在 .NET Core 8 中创建实时通知,确保应用程序保持响应性和吸引力。
为什么使用 SignalR?
实时通信: 它支持实时更新,这对于现代动态应用程序(如聊天系统、游戏、实时体育更新和协作应用程序)至关重要。
简化开发: SignalR 提供了一个简单的 API,它抽象了实时通信的复杂性,使开发人员能够更轻松地实现这些功能。
跨平台: SignalR 支持多个平台,包括 .NET、JavaScript 和 Xamarin,从而在不同类型的应用程序中具有广泛的适用性。
自动重新连接: SignalR 在连接断开的情况下处理自动重新连接,确保实时通信的可靠性。
可扩展性: SignalR 是可缩放的,并且可以与其他服务(如 Azure SignalR 服务)结合使用,后者可处理横向扩展需求并提供高可用性。
创建 SignalR 服务器
安装
首先,让我们安装 SignalR 包。您可以通过 Visual Studio 中的 NuGet 包管理器控制台使用以下命令执行此操作:
Install-Package Microsoft.AspNetCore.SignalR
或者,您也可以使用 NuGet 包管理器 UI 通过搜索“Microsoft.AspNetCore.SignalR”来安装它。
创建 ASP.Net Core Web API 项目
第 1 步
打开 Visual Studio 并单击 Create New Project。
步骤 2
选择 ASP.NET Core Web API 模板,如下图所示,然后单击 下一步 按钮。
步骤 3
通过指定项目名称和要保存项目的位置来配置项目。
步骤 4
选择要使用的 .Net Core 框架版本,然后单击“创建”按钮。
步骤 5
转到 Program.cs 并为 SignalR 添加服务
builder.Services.AddSignalR();
步骤 6
添加类 StockHub.cs
using Microsoft.AspNetCore.SignalR;
namespace SignalRWebAPI
{
public class StockHub:Hub
{
public async Task SendStockPrice(string stockName, decimal price)
{
await Clients.All.SendAsync("ReceiveStockPrice", stockName, price);
}
public override async Task OnConnectedAsync()
{
string connectionId = Context.ConnectionId;
await base.OnConnectedAsync();
}
}
}
步骤 7
转到 Program.cs 并映射 stockHub
app.MapHub<StockHub>("/hubs/stock");
步骤 8 后台服务持续推送通知 添加类 Worker.cs
using Microsoft.AspNetCore.SignalR;
namespace SignalRWebAPI
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IHubContext<StockHub> _stockHub;
private const string stockName = "Basic Stock Name";
private decimal stockPrice = 100;
public Worker(ILogger<Worker> logger, IHubContext<StockHub> stockHub)
{
_logger = logger;
_stockHub = stockHub;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
Random rnd = new Random();
decimal stockRaise = rnd.Next(1, 10000);
//List of stock names
string[] stockNames = { "Apple", "Microsoft", "Google", "Amazon", "Facebook" };
//Random stock name
var stockName = stockNames[rnd.Next(0, stockNames.Length)];
//Send Notification
await _stockHub.Clients.All.SendAsync("ReceiveStockPrice", stockName, stockRaise);
_logger.LogInformation("Sent stock price: {stockName} - {stockRaise}", stockName, stockRaise);
await Task.Delay(4000, stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending stock price");
}
}
}
}
}
在 Program.cs 中添加此后台服务
builder.Services.AddHostedService<Worker>();
SignalR 接收器
服务器端
单独添加新的控制台项目,或在同一解决方案文件中添加新的控制台项目。
从 Program.cs 中删除 Console App 中的所有代码,并粘贴以下代码。
using Microsoft.AspNetCore.SignalR.Client;
//here is SignalR Sender URL
string hubUrl = "https://localhost:7091/hubs/stock";
var hubConnection = new HubConnectionBuilder()
.WithUrl(hubUrl)
.Build();
// Register a handler for messages from the SignalR hub
// "ReceiveStockPrice" is the topic to which SignalR sending the singnals
hubConnection.On<string, decimal>("ReceiveStockPrice", (stockName, stockPrice) =>
{
Console.WriteLine($"Message received--> Stock Name: {stockName} Stock Price: {stockPrice}");
});
try
{
// Start the connection
hubConnection.StartAsync().Wait();
Console.WriteLine("SignalR connection started.");
}
catch (Exception ex)
{
Console.WriteLine($"Error connecting to SignalR: {ex.Message}");
throw;
}
//Create a cancellation token to stop the connection
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
//hubConnection.StopAsync().Wait();
var cancellationToken = cancellationTokenSource.Token;
// Handle Ctrl+C to gracefully shut down the application
Console.CancelKeyPress += (sender, a) =>
{
a.Cancel = true;
Console.WriteLine("Stopping SignalR connection...");
cancellationTokenSource.Cancel();
};
try
{
// Keep the application running until it is cancelled
await Task.Delay(Timeout.Infinite, cancellationToken);
}
catch (TaskCanceledException)
{
}
// Stop the connection gracefully
await hubConnection.StopAsync();
Console.WriteLine("SignalR connection closed.");
客户端
Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SignalR Client</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.18/signalr.min.js"></script>
</head>
<body>
<h1>Stock Prices</h1>
<div id="stockPrices"></div>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("https://localhost:7091/hubs/stock")
.build();
connection.on("ReceiveStockPrice", (stockName, stockPrice) => {
const stockPricesDiv = document.getElementById("stockPrices");
const newPrice = document.createElement("div");
newPrice.textContent = `Stock received: ${stockName} - ${stockPrice}`;
stockPricesDiv.appendChild(newPrice);
});
connection.start()
.then(() => {
console.log("SignalR connection started.");
})
.catch(err => {
console.error("Error connecting to SignalR: ", err);
});
window.addEventListener("beforeunload", () => {
connection.stop().then(() => {
console.log("SignalR connection stopped.");
}).catch(err => {
console.error("Error stopping SignalR connection: ", err);
});
});
</script>
</body>
</html>
现在在 Web 和控制台应用程序上接收通知。
实现实时通知终端节点
app.MapPost("sendStockNotification", async (
string stockName,
decimal price,
IHubContext<StockHub> context) =>
{
await context.Clients.All.SendAsync("ReceiveStockPrice", stockName, price);
});
当客户端向终端节点发送包含股票名称和价格的 POST 请求时,此代码会将股票价格更新实时广播到所有连接的客户端。客户端将通过他们订阅的方法接收此更新。
如果你喜欢我的文章,请给我一个赞!谢谢