一、使用场景:
客户端与服务端实时通讯,服务端主动向客户端发送消息;
二、分类:
1 websockets:单个TCP连接上的全双工通信协议;
2 SSE:Server-Sent Events:单向,服务器端主动向客户端推送消息;
3 Long Polling(长轮询)
三、问题
1 本机调试出现以下错误:
POST http://localhost:10145/NotificationHub/negotiate?negotiateVersion=1 500 (Internal Server Error)
Error: Failed to complete negotiation with the server: Error: System.MissingMethodException: Method not found: 'Microsoft.AspNetCore.Http.Features.IFeatureCollection Microsoft.AspNetCore.Connections.ConnectionContext.get_Features()'. at Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext..ctor(String connectionId, String connectionToken, ILogger logger) at Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager.CreateConnection(PipeOptions transportPipeOptions, PipeOptions appPipeOptions, Int32 negotiateVersion)
---原因:通过Nuget安装的signalr包错误
---解决方案:使用Microsoft.AspNet.SignalR.Core包
2 Clients.All.SendAsync出现未将对象引用设置到对象的异常:Clients未Null值
--原因:之前的写法hub对象与客户端的hub对象不是同一个,需要手动依赖注入一下
原写法:public class NotificationHub:Hub
{
public async Task SendNotification(string message)
{
var connId = "test:";//this.Context.ConnectionId;
var msg = $"{connId}{DateTime.Now.ToString()}:{message}";
await Clients.All.SendAsync("ReceiveNotification", msg);
}
}
---解决方案:
public class NotificationHub:Hub
{
private IHubContext<NotificationHub> _notificationHub { get; }
public NotificationHub(IHubContext<NotificationHub> notificationHub)
{
this._notificationHub = notificationHub;
}
public async Task SendNotification(string message)
{
var connId = "test:";//this.Context.ConnectionId;
var msg = $"{connId}{DateTime.Now.ToString()}:{message}";
await _notificationHub.Clients.All.SendAsync("ReceiveNotification", msg);
}
}
四、完整代码:
1 前端显示页-razor页面 Index.cshtml
@page
@model TodoApi.Pages.SignalR.IndexModel
@{
}
<h1>SignalR for Notification Demo</h1>
<div id="notification-container"></div>
@section Scripts{
<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script>
//创建signalr hub客户端链接
var connection = new signalR.HubConnectionBuilder()
.withUrl("/NotificationHub")
.build();
//监听接收通知的回调函数
connection.on("ReceiveNotification", function (message) {
//在页面上显示接收到的通知
displayNotification(message);
});
//连接建立后的回调函数
connection.start().then(function () {
console.log("Connected to the SignalR Hub!");
}).catch(function (err) {
return console.error(err.toString());
});
//显示通知的函数
function displayNotification(message) {
//更新页面元素
var notificationContainer = document.getElementById("notification-container");
notificationContainer.innerHTML += "<p>" + message + "</p>";
}
</script>
}
2 后端Hub类:
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TodoApi.SignalR
{
public class NotificationHub:Hub
{
private IHubContext<NotificationHub> _notificationHub { get; }
public NotificationHub(IHubContext<NotificationHub> notificationHub)
{
this._notificationHub = notificationHub;
}
public async Task SendNotification(string message)
{
var connId = "test:";//this.Context.ConnectionId;
var msg = $"{connId}{DateTime.Now.ToString()}:{message}";
await _notificationHub.Clients.All.SendAsync("ReceiveNotification", msg);
}
}
}
3.后端调用方法和依赖注入:
依赖注入Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<NotificationHub>("/NotificationHub");
endpoints.MapHub<NotificationHub>("/ChatHub");
});
}
public void ConfigureServices(IServiceCollection services)
{
//添加signalr服务
services.AddSignalR();
//添加依赖注入项:controller的构造函数中传递两个参数
services.AddScoped<TodoContext>();
services.AddScoped<NotificationHub>();
}
后端调用:
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
private readonly NotificationHub _hubContext;
public TodoItemsController(TodoContext context,NotificationHub hubContext)
{
_context = context;
_hubContext = hubContext;
}
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDto>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
await _hubContext.SendNotification($"获取了一次数据:{id}");
return ItemToDTO(todoItem);
}
}