SignalR for ASP.NET Core是一个开源库,可简化向应用添加即时通讯。支持多端与中心端即时通讯,实现消息相互推送。
配置 SignalR 中心
ASP.NET Core框架已经内置SignalR,我们可以直接注入使用。
services.AddSignalR();
注入之后重载 Hub 接口,实现上线、下线和历史消息推送。
public class MessageHub : Hub<IMessageClient>
{
private readonly ILogger<MessageHub> logger;
private readonly IHttpContextAccessor httpContextAccessor;
/// <summary>
/// MessageHub
/// </summary>
public MessageHub(ILogger<MessageHub> logger,
IHttpContextAccessor httpContextAccessor)
{
this.logger = logger;
this.httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// 上线
/// </summary>
public override Task OnConnectedAsync()
{
string connId = Context.ConnectionId;
logger.LogInformation($"SignalR:客户端[{connId}]开始连接");
try
{
string token = httpContextAccessor.HttpContext.Request.Query["access_token"];
var result = JWTExtension.ValidateJwtToken(token);
if (result.Status)
{
dynamic payload = JsonConvert.DeserializeObject(result.Data);
string userId = payload.UserId;
string userName = payload.UserName;
//上线并推送历史消息
using (var efDbContext = (EfDbContext)ServiceLocator.Instance.CreateScope().ServiceProvider.GetService(typeof(EfDbContext)))
{
SignalRClientEntity client = efDbContext.SignalRClient.FirstOrDefault(x => x.UserId == userId);
if (client != null)
{
efDbContext.Remove(client);
}
efDbContext.Add(new SignalRClientEntity
{
Id = ZnResponse.GuId(),
UserId = userId,
UserName = userName,
ConnectionId = connId,
ConnectedDate = DateTime.Now
});
efDbContext.SaveChanges();
List<MessageDto> messages = efDbContext.SysMessage.Where(x => x.CategoryId == (int)MessageCategory.Broadcast || x.ReceiveUserId == userId)
.OrderByDescending(x => x.CreatedDate)
.ToList();
Clients.Client(connId).ReceiveMessage(new ResultModel<List<MessageDto>>(messages.Count > 0, messages));
logger.LogInformation($"SignalR:客户端[{connId}]历史消息推送成功");
}
}
}
catch
{
logger.LogInformation($"SignalR:客户端[{connId}]历史消息推送失败");
}
return base.OnConnectedAsync();
}
/// <summary>
/// 下线
/// </summary>
public override Task OnDisconnectedAsync(Exception exception)
{
string connId = Context.ConnectionId;
try
{
using (var efDbContext = (EfDbContext)ServiceLocator.Instance.CreateScope().ServiceProvider.GetService(typeof(EfDbContext)))
{
SignalRClientEntity client = efDbContext.SignalRClient.FirstOrDefault(x => x.ConnectionId == connId);
if (client != null)
{
efDbContext.Remove(client);
efDbContext.SaveChanges();
}
logger.LogInformation($"SignalR:客户端[{connId}]删除成功");
}
}
catch
{
logger.LogInformation($"SignalR:客户端[{connId}]删除失败");
}
logger.LogInformation($"SignalR: 客户端[{connId}]关闭连接");
return base.OnDisconnectedAsync(exception);
}
}
配置 Vue 客户端
SignalR支持多种客户端连接。本文以 Vue 为例示范。
引用SignalR官方NPM包
npm install @microsoft/signalr -s
封装一下支持多组件调用
import * as signalR from "@microsoft/signalr";
import { getToken } from "@/utils/token";
const token = getToken();
const url = window.config.signalRUrl;
const connection = new signalR.HubConnectionBuilder()
.withUrl(url, { accessTokenFactory: () => token })
.configureLogging(signalR.LogLevel.Error)
.withAutomaticReconnect([0, 3000, 5000, 10000, 15000, 30000])
.build();
connection
.start()
.then(() => {
console.log("SignalR is ready");
})
.catch((err) => {
return console.error(err.toString());
});
export default {
receiveMessage(callback) {
connection.on("ReceiveMessage", (result) => {
callback(result);
});
},
sendMessage(message, callback) {
connection
.invoke("SendMessage", message)
.then((result) => {
callback(result);
})
.catch((err) => {
return console.error(err.toString());
});
},
};
在 Vue 监听收发
- 接收消息
signalR.receiveMessage((result) => {
this.messageList = this.messageList.concat(result.data);
});
- 发送消息
says(event) {
signalR.sendMessage(this.message, (result) => {});
event.preventDefault();
}