实现输入提示 layui_ASP.NET Core SignalR :学习消息通讯,实现一个消息通知

什么是 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,我就是为了让前端和后端统一下。刚开始Hub我纠结了好久,不知道怎么用,最后我手动做了下,认为它只是为了方便前端和后端统一。

如果你只是简单的继承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,自己定义一个接口,提过方法InvokeMessage供前前端调用。

/// /// 客户端js调用方法/// public interface ISingalrClient    {Task InvokeMessage(Message sendMessage);    }public class SingalrService : Hub    {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 _hubContext;private ISingalrSvc _singalrSvc;public SingalrController(IHubContext hubContext, ISingalrSvc singalrSvc)  {      _hubContext = hubContext;      _singalrSvc = singalrSvc;  }/// /// 查询未处理数量/// /// ///   [HttpGet("{account}")]public async Task NewsCount(string account)  {      Message sendMessage = new Message();      sendMessage.Data = "11";//刚已经讲了,用户加载首页的时候已经把connectionid和account存入到了内存里面,现在再取用户相关的connectionID,如果直接调用Clinets.ALL就是给所有客户端发送消息      IReadOnlyList connectionIds = (IReadOnlyList)_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); }, }); });

原文地址:

https://www.cnblogs.com/MrHanBlog/p/11996689.html

58dc8f7cdb335d45da757307739053f0.png

点击【在看】与好友一起分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/534277.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux课堂笔记(5)

vi编辑器 1,vi版本:vi —— vim ps:查看进程 进程 ID 时间 终端 进程名称 ps -t 终端(查看指定终端进程) vi 回车(提示vi版本信息) 2,vi特点 (1)…

两个字和三个字一样宽_武汉国庆出行 记好三个字

武汉国庆出行国庆黄金周带你一起见证奇迹乐在武汉国庆去哪儿玩?记住:三 个 字欢乐谷记住【欢乐谷】三个字,你就不会迷路!不算白来。欢乐谷有啥好?免费!国庆期间,欢乐谷面向全国人民免费开放&…

linux课堂笔记(6)

设置文件访问权限: 1,通过字母设置:参数 字母 (1)用户类型: u 拥有者 g 同组用户 o 其他用户 a所有用户 (2)权限:r 读 w 写 x执行 注:目录权…

什么标签用于在表单中构建复选框_UI/UX笔记之如何设计好表单

原文链接:UI/UX笔记之如何设计好表单 | 须臾所学免费设计资源网​presentationvip.com无论是注册流程,多视图步骤程序还是单调的数据输入界面,表单都是数字产品设计中最重要的组成部分之一。本文重点介绍表单设计的常见事项。请记住&#xff…

linux课堂笔记(7)

编译器&#xff1a;gcc 格式&#xff1a;gcc [参数] <源程序> gcc test.c 注&#xff1a;默认生成可执行程序a.out&#xff08;windows默认test.exe&#xff09; ./a.out gcc -o 可执行文件 源程序 gcc -o test.exe test.c&#xff08;windows&#xff09; g…

mqtt 发送消息过多_阿里云MQTT服务端注解式消息处理分发与同步调用实践小结

一、前言前段时间公司预研了设备app端与服务端的交互方案&#xff0c;出于多方面考量最终选用了阿里云的微服务队列MQTT方案&#xff0c;基于此方案&#xff0c;本人主要实践有&#xff1a;1. 封装了RocketMQ实现MQTT订阅与发布的实现细节&#xff1b;2. 实现了注解式分发处理&…

linux课堂笔记(8)

磁盘分区工具&#xff08;parted&#xff09; 1&#xff0c;语法&#xff1a; parted 磁盘 注&#xff1a;磁盘类型&#xff08;根据接口划分&#xff09; &#xff08;1&#xff09;IDE-hd&#xff0c;hda&#xff0c;hdc&#xff08;光驱&#xff09;&#xff0c;hdd 加…

css引入矢量图标_WEB 项目开发中的图标管理解决方案

相信很多前端开发人员在项目开发过程中都会遇到这样一个问题&#xff1a;页面的图标发生改动&#xff0c;需要往图标文件中追加新的图标&#xff0c;却因为图标文件已经打包好而无从下手&#xff0c;重新制作一份图标文件吧&#xff0c;要考虑替换整个项目的图标&#xff0c;工…

判断按键值_Pygame(九)按键事件(2)

Pygame(九)按键事件(2)前情提要前情提要作业解析完整代码# /usr/bin/python3# Author: 爱编程的章老师# Time: 2021/1/7 0007# E-mail: Bluesand2010163.comimport pygame, sys, timefrom random import randintdef homework():作业 挡板接球 一个宽100 ,高10的棕色挡板,…

动态添加input_前端提效必备:动态模版生成

前言在日常开发中&#xff0c;我们需要不停的新建页面和组件。以 Vue 项目为例&#xff0c;我们在新建一个页面的时候&#xff0c;需要经历一遍又一遍重复的过程&#xff1a;1、先新建一个文件夹2、然后新建一个 .vue 文件&#xff0c;写上 、", "" ],"…

在dom最前面插入_JavaScript中的DOM

1. 关于DOM文档对象模型(DocumentObject Model)&#xff0c;是基于浏览器编程的一套API接口&#xff0c;W3C出台的推荐标准&#xff0c;每个浏览器都有一些细微的差别&#xff0c;其中以Mozilla(火狐)的浏览器最与标准接近。通过 DOM&#xff0c;可以访问所有的 HTML元素&#…

python 快速删除程序_如何快速一次性卸载所有python包(第三方库)呢

很多时候我们都会有一个麻烦事&#xff0c;就是打开pycharm或者VScode等的时候&#xff0c;都有可能因为自己电脑上面安装的第三方模块过多&#xff0c;导致电脑很卡&#xff0c;风扇转速不停地增加&#xff0c;这时候我们就会想去卸载一些不太用得着的第三方模块&#xff0c;但…

python正则表达式修饰符_Python正则表达式

正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的检查一个字符串是否与某种模式匹配。re 模块使 Python 语言拥有全部的正则表达式功能。compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。r…

当代最值得收藏的画家作品_当代最具潜力和收藏价值的十大画家

原标题&#xff1a;当代最具潜力和收藏价值的十大画家当代从事绘画的人成千上万&#xff0c;哪些名家作品值得收藏&#xff1f;当前都有哪些“潜力股”&#xff0c;相关专家综合市场分析&#xff0c;纯从艺术水准上列出值得收藏的“潜力股”&#xff0c;供爱好书画收藏的各界人…

备抵附加账户的期末余额_会计账户的分类(二)

待摊费用账户的格式和运用方法同盘存类账户&#xff0c;即&#xff1a;期初如果有余额在借方&#xff0c;本期发生额的增加数在借方&#xff0c;本期发生额的减少数在贷方&#xff0c;期末如果有余额在借方。预提费用账户是指用来核算和监督按规定预先提取计入当期&#xff0c;…

mysql union all 别名_mysql union和union all

如下先创建2个表&#xff0c;aa bb.CREATE table aa(uid int(20) not null,name VARCHAR(30) not null)engineinnodb default charsetutf8mb4 COLLATE utf8mb4_general_ci;INSERT INTO aa(uid, name) VALUES (10, 张芳);INSERT INTO aa(uid, name) VALUES (11, 王凯);INSERT IN…

MySQL不走联合索引_mysql group by 多列优化思路?为什么不走联合索引?

explain SELECT a, b, COUNT(*) FROM tbnameGROUP BY a, border by a DESClimit 1a 和 b 列已经设置联合索引&#xff0c; 为什么这种操作也会执行全表扫描呢&#xff1f;explain SELECT a, b FROM tbnameGROUP BY a, border by a DESC去掉 COUNT 和 limit 则走索引没有扫描&am…

mysql ddl 锁_MySQL Online DDL导致全局锁表案例分析

MySQL Online DDL导致全局锁表案例分析我这边遇到了什么问题?线上给某个表执行新增索引SQL, 然后整个数据CPU打到100%, 连接数暴增到极限, 最后导致所有访问数据库的应用都奔溃.SQL如下:ALTER TABLE bookADD INDEX idx_sub_title (sub_title ASC);能看到什么?10063293, root,…

ci框架 乱码 mysql_mysql容器乱码问题

在docker-compose.yml文件中定义mysql导入utf-8的万国码services:mysql:image:mysql:5.7# command: [--character-set-serverutf8mb4, --collation-serverutf8mb4_unicode_ci]volumes:-./data/docker/mysql:/var/lib/mysql-./mysql/:/docker-entrypoint-initdb.d/-./conf/mys…

mysql分表 查询 优化_MySQL性能管理及架构(查询优化、分库分表)一遍文章搞定...

相关配置参数&#xff1a;slow_query_log # 启动停止记录慢查日志&#xff0c;慢查询日志默认是没有开启的可以在配置文件中开启(on)slow_query_log_file # 指定慢查日志的存储路径及文件&#xff0c;日志存储和数据从存储应该分开存储long_query_time # 指定记录慢查询日志SQL…