什么是 SignalR

目前我用业余时间正在做一个博客系统,其中有个功能就是评论通知,就是假如A用户评论B用户的时候,如果B用户首页处于打开状态,那么就会提示B用户有未读消息。暂时用SignalR来实现这个功能。我也是看了两天的资料才明白怎么去使用。

关于SignalR的理论知识可以去官网或者百度,我这里只是结合自己的功能来分享下,如果有错,请原谅指出。

下载js

SignalR是需要微软提供的js,因为我的项目是前后端分离的,所以我是单独下载到一个文件夹,然后复制js到我的前端项目里。只需要signalr.js

页面加载创建连接

 //创建连接  
var connection = new signalR.HubConnectionBuilder().withUrl('http://127.0.0.1:5000/chatHub').build();
//ajax执行成功执行
  $.ajax({
            success: function (response) {              
                     connection.start().then(function () {
                            connection.invoke('SetConnectionMaps', response.data.account);
                    }
                },
            });
     

首先你要了解到SignalR基本运行的原理,官网:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-3.1&tabs=visual-studio

你可以直接继承Hub这个类,我这里用的是强类型Hub<T>,我就是为了让前端和后端统一下。刚开始Hub<T>我纠结了好久,不知道怎么用,最后我手动做了下,认为它只是为了方便前端和后端统一。

如果你只是简单的继承Hub类,你就必须调用SendAsync方法,并且指定前端接收触发的方法名称“InvokeMessage”,如果你后端和前端名字对应不上,就会有问题。

public class SingalrService : Hub
{
private ISingalrSvc _singalrSvc;
public SingalrService(ISingalrSvc singalrSvc)
{
_singalrSvc = singalrSvc;
} public async Task SendMessageAsync(Message sendMessage)
{
await Clients.All.SendAsync("InvokeMessage",sendMessage);
} public void SetConnectionMaps(string account)
{
string connectionid = Context.ConnectionId;
_singalrSvc.SetConnectionMaps(connectionid, account);
}
public override Task OnDisconnectedAsync(Exception exception)
{
_singalrSvc.Remove(Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
}

所以有了强类型Hub<T>,自己定义一个接口,提过方法InvokeMessage供前前端调用。

    /// <summary>
/// 客户端js调用方法
/// </summary>
public interface ISingalrClient
{
Task InvokeMessage(Message sendMessage);
}
public class SingalrService : Hub<ISingalrClient>
{
private ISingalrSvc _singalrSvc;
public SingalrService(ISingalrSvc singalrSvc)
{
_singalrSvc = singalrSvc;
}
public void SetConnectionMaps(string account)
{
string connectionid = Context.ConnectionId;
_singalrSvc.SetConnectionMaps(connectionid, account);
}
//连接中断时执行,微软这样描述的:
//重写 OnDisconnectedAsync 虚方法,以便在客户端断开连接时执行操作。 如果客户端故意断开连接(例如,通过调用 connection.stop()),exception 参数将 null
//但是,如果客户端由于错误(例如网络故障)而断开连接,则 exception 参数将包含描述失败的异常
public override Task OnDisconnectedAsync(Exception exception)
{
_singalrSvc.Remove(Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
}

这个时候一个用户打开了首页,然后首页有个js方法来初始化连接,同一个页面内的connectionid是一样的,每次刷新或新打开一个窗口的新页面的connectionid是不一样的,并且你刷新页面或者关掉会认为是连接中断,会执行OnDisconnectedAsync方法,这个方法时SingalR自带的,它是个虚方法,你也可以重写,就像我一样。我这里的代码逻辑是将连接id和当前登录人作为键值对存入内存,然后用户关掉页面就会执行OnDisconnectedAsync方法,将相关的coonectionid从内存删掉:

layui.use(['element', 'layer'], function () {          
var element = layui.element;
element.render('nav');
initLoad();
//初始化连接,每个页面的connection的connectionid是一样的,但是每次创建的不一样
var connection = new signalR.HubConnectionBuilder().withUrl('http://127.0.0.1:5000/chatHub').build();
//绑定后台触发的方法,前面已经讲过了,具体业务还没实现,
connection.on('InvokeMessage', (reviceMessage) => {
var v = reviceMessage;
});
$.ajax({
url: url + 'user/userInfo',
type: 'get',
dataType: 'json',
beforeSend: function (xhr) {
doBeforeSend(xhr);
},
success: function (response) {
if (response.code == '1') {
$("#nologin").show();
$("#user").hide();
}
else {
$("#nologin").hide();
$("#user").show();
$("#photo").attr('src', response.data.headPhoto);
//连接开始
connection.start().then(function () {
//调用后台方法,不是api接口,将当前登录人账号传过去
connection.invoke('SetConnectionMaps', response.data.account);
})
}
},
complete: function (xhr) {
doComplete(xhr);
},
});
});

这个时候连接已经创建完成,并且用户并没有关闭首页,连接一直处于连接状态。这个时候另一个用户打开了一篇文章详情,并且对它评论提交内容后,我让它触发了一个连接SingalR的事件,

    form.on('submit(review)', function (data) {
loading = layer.load(2);
var commentModel = {
'Content': data.field.desc,
}
$.ajax({
url: url + 'article/review/' + id,
contentType: 'application/json; charset=utf-8',
type: 'post',
datatype: 'json',
data: JSON.stringify(commentModel),
beforeSend: function (xhr) {
doBeforeSend(xhr);
},
success: function (response) {
if (response.code == 0) {
//另一个用户创建了连接
var connection = new signalR.HubConnectionBuilder().withUrl('http://127.0.0.1:5000/chatHub').build();
connection.start().then(function () {
var apiRoute=url+'Singalr/admin';//admin是我设置死的,实际应该是自己判断,会调用下面的api,[Route("api/[controller]")],SingalR也是支持api调用的
var token=localStorage.getItem('token');
fetch(apiRoute,{
method:'get',
headers:{
'Authorization':'Bearer ' + token
}
})
event.preventDefault();
})
layer.close(loading);
} else {
layer.close(loading);
layer.msg("评论失败", {
icon: 5
});
}
},
complete: function (xhr) {
doComplete(xhr);
},
})
    [ApiController]
public class SingalrController : ControllerBase
{
private IHubContext<SingalrService, ISingalrClient> _hubContext;
private ISingalrSvc _singalrSvc;
public SingalrController(IHubContext<SingalrService, ISingalrClient> hubContext, ISingalrSvc singalrSvc)
{
_hubContext = hubContext;
_singalrSvc = singalrSvc;
}
/// <summary>
/// 查询未处理数量
/// </summary>
/// <param name="account"></param>
/// <returns></returns>
[HttpGet("{account}")]
public async Task NewsCount(string account)
{
Message sendMessage = new Message();
sendMessage.Data = "11";
//刚已经讲了,用户加载首页的时候已经把connectionid和account存入到了内存里面,现在再取用户相关的connectionID,如果直接调用Clinets.ALL就是给所有客户端发送消息
IReadOnlyList<string> connectionIds = (IReadOnlyList<string>)_singalrSvc.GetConnectionIds(account);
await _hubContext.Clients.Clients(connectionIds).InvokeMessage(sendMessage);
}
}

这个时候调用了这个api执行了里面的_hubContext.Clients.Clients(connectionIds).InvokeMessage(sendMessage),connectionIds是根据业务逻辑所判断的触发的那些客户端;然后前端会根据方法名响应对应的js代码,如下

layui.use(['element', 'layer'], function () {
var element = layui.element;
element.render('nav');
initLoad();
var connection = new signalR.HubConnectionBuilder().withUrl('http://127.0.0.1:5000/chatHub').build(); //我认为是一个用来侦听服务端方法的js
connection.on('InvokeMessage', (reviceMessage) => {
var v = reviceMessage;
//响应后端方法成功后,就开始自己的业务逻辑
});
$.ajax({
url: url + 'user/userInfo',
type: 'get',
dataType: 'json',
beforeSend: function (xhr) {
doBeforeSend(xhr);
},
success: function (response) {
if (response.code == '1') {
$("#nologin").show();
$("#user").hide();
}
else {
$("#nologin").hide();
$("#user").show();
$("#photo").attr('src', response.data.headPhoto);
connection.start().then(function () {
connection.invoke('SetConnectionMaps', response.data.account);
})
}
},
complete: function (xhr) {
doComplete(xhr);
},
});
});

  

游戏也能赚钱?如果你热爱游戏,并且想通过游戏赢得零花钱,5173是个不错的选择  http://www.5173.com/?recommenduserid=US15061749098191-04F6

ASP.NET Core SignalR :学习消息通讯,实现一个消息通知的更多相关文章

  1. ASP.NET Core SignalR中的流式传输

    什么是流式传输? 流式传输是这一种以稳定持续流的形式传输数据的技术. 流式传输的使用场景 有些场景中,服务器返回的数据量较大,等待时间较长,客户端不得不等待服务器返回所有数据后,再进行相应的操作.这时 ...

  2. Asp.Net Core SignalR 用泛型Hub优雅的调用前端方法及传参

    继续学习 最近一直在使用Asp.Net Core SignalR(下面成SignalR Core)为小程序提供websocket支持,前端时间也发了一个学习笔记,在使用过程中稍微看了下它的源码,不得不 ...

  3. Asp.Net Core SignalR 与微信小程序交互笔记

    什么是Asp.Net Core SignalR Asp.Net Core SignalR 是微软开发的一套基于Asp.Net Core的与Web进行实时交互的类库,它使我们的应用能够实时的把数据推送给 ...

  4. ASP.NET Core SignalR

    ASP.NET Core SignalR 是微软开发的一套基于ASP.NET Core的与Web进行实时交互的类库,它使我们的应用能够实时的把数据推送给Web客户端. 功能 自动管理连接 允许同时广播 ...

  5. Asp.Net Core WebApi学习笔记(四)-- Middleware

    Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...

  6. ASP.NET Core SignalR:基础概述

    一.简介 ASP.NET Core SignalR 是一个开源代码库,它简化了向应用添加实时 Web 功能的过程. 实时 Web 功能使服务器端代码能够即时将内容推送到客户端. SignalR 的适用 ...

  7. 使用websocket连接(对接)asp.net core signalr

    使用通用websocket连接asp.net core signalr 一.背景介绍 signalr的功能很强大,可以为我们实现websocket服务端节省不少的时间.但是可能由于不同的环境,我们在对 ...

  8. ASP.NET CORE使用WebUploader对大文件分片上传,并通过ASP.NET CORE SignalR实时反馈后台处理进度给前端展示

    本次,我们来实现一个单个大文件上传,并且把后台对上传文件的处理进度通过ASP.NET CORE SignalR反馈给前端展示,比如上传一个大的zip压缩包文件,后台进行解压缩,并且对压缩包中的文件进行 ...

  9. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(中)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 四.创建一个Blazor应用程序 1. 第一种创 ...

  10. [asp.net core]SignalR一个例子

    摘要 在一个后台管理的页面想实时监控一些操作的数据,想到用signalR. 一个例子 asp.net core+signalR 使用Nuget安装包:Microsoft.AspNetCore.Sign ...

随机推荐

  1. Redis过期--淘汰机制的解析和内存占用过高的解决方案

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! Red ...

  2. (十二)golang--进制和位运算

    1.基本进制 (1)二进制:0,1,满2进1 在golang中,不能直接使用一个二进制表示一个整数,可以用八进制.十进制和十六进制表示 (2)十进制:0-9,满10进1 (3)八进制:0-7,满8进1 ...

  3. 【html css js】实现一个简易日历

    ——[效果预览] 实现了日历最基础的功能,当前日期红色显示,可通过上方的左右按钮查看上一月或下一月的日期. ——[代码部分] 1. HTML <body> <div class=&q ...

  4. java 深拷贝与浅拷贝

    yls 2019年11月07日 拷贝分为引用拷贝和对象拷贝 深拷贝和浅拷贝都属于对象拷贝 浅拷贝:通过Object默认的clone方法实现 实现Cloneable接口 public class She ...

  5. pat 1042 Shuffling Machine(20 分)

    1042 Shuffling Machine(20 分) Shuffling is a procedure used to randomize a deck of playing cards. Bec ...

  6. 领扣(LeetCode)删除注释 个人题解

    给一个 C++ 程序,删除程序中的注释.这个程序source是一个数组,其中source[i]表示第i行源码. 这表示每行源码由\n分隔. 在 C++ 中有两种注释风格,行内注释和块注释. 字符串// ...

  7. 线程同步&线程池

    线程同步&线程池 线程同步 线程不同步会出现的问题: 当多个线程操作同一资源时,会出现重复操作和和操作不存在的资源的问题,为了规避这一问题就需要线程的同步操作来实现资源的共同使用. 线程同步: ...

  8. python描述:链表

    单链表结构: 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点 ...

  9. python_08

    一.作业 ''' 主页: 图标地址.下载次数.大小.详情页地址 详情页: 游戏名.好评率.评论数.小编点评.下载地址.简介.网友评论.1-5张截图链接地址. https://www.wandoujia ...

  10. Fragment的生命周期(与Activity的对比)

    Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期.官网这张图很好的说明了两者生命周期的关系: 可以看到Fragment比Act ...