tnblog
首页
视频
资源
登录

.net6 Signalr+Vue3 的运用(下)

4566人阅读 2023/2/1 17:29 总访问:3505337 评论:0 收藏:0 手机
分类: .net后台框架

.netcore

.net6 Signalr+Vue3 的运用(下)


上篇链接:https://www.tnblog.net/hb/article/details/7961

SignalR 中的用户

SignalR 中的单个用户可以与一个应用建立多个连接。
举例:当你手机和电脑连接到SignalR时,识别到当前用户多个设备可以同时发送消息。
可以通过中心内的Context.UserIdentifier属性访问连接的用户标识符。

示例


接下来我们结合上篇,在ClientHubController控制器的基础上,添加一个SendCustomUserMessage2接口专门对单一用户关联的所有设备发送消息。

  1. /// <summary>
  2. /// 发送指定消息给指定的用户
  3. /// </summary>
  4. /// <param name="userid"></param>
  5. /// <param name="date"></param>
  6. /// <param name="hubContext"></param>
  7. /// <returns></returns>
  8. [HttpGet("SendCustomUserMessage2", Name = "SendCustomUserMessage2")]
  9. public async Task<IActionResult> SendCustomUserMessage2(
  10. string username,
  11. string date,
  12. [FromServices] IHubContext<ChatHub, IChatClient> hubContext
  13. )
  14. {
  15. await hubContext.Clients.User(username).SendCustomUserMessage(date);
  16. return Ok("Send Successful!");
  17. }


测试两个页面登录同一个bob用户,然后在服务器端swagger调用方法,两个页面将同时收到消息。


那么有人要问了能不能不以UserIdentifier作为标识换成其他标识,比如通过邮箱来进行标识?
可以进行改变授权的逻辑吗?
当然可以。

自定义设备标识

可以通过实现IUserIdProvider接口中的GetUserId方法,来进行设备的区分,我这里通过邮箱的方式来作为我们区分的标识。


首先我们在后端定义EmailBasedUserIdProvider类实现IUserIdProvider接口,然后我们再对其重新进行依赖注入。

  1. public class EmailBasedUserIdProvider : IUserIdProvider
  2. {
  3. public string? GetUserId(HubConnectionContext connection)
  4. {
  5. return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
  6. }
  7. }
  1. builder.Services.AddSingleton<IUserIdProvider, EmailBasedUserIdProvider>();


然后我们在MyJWTBearer中添加一个Email的Claim参数。

  1. var claims = new[] {
  2. new Claim(ClaimTypes.NameIdentifier, httpContext.Request.Query["user"]),
  3. new Claim(ClaimTypes.Email, $"{httpContext.Request.Query["user"]}@tnblog.com"),
  4. };


这样就可以了,测试一下。

自定义授权逻辑


接下来我们通过定义TnblogRequirement实现自定义授权,然后添加一下自定义授权策略一下,并在SendMessage方法上添加自定义授权。

  1. public class TnblogRequirement : AuthorizationHandler<TnblogRequirement, HubInvocationContext>, IAuthorizationRequirement
  2. {
  3. protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TnblogRequirement requirement, HubInvocationContext resource)
  4. {
  5. // 首先用户的授权Identity 属性不能为空
  6. // 然后我们禁用cc@tnblog.com邮箱的账户
  7. if (
  8. context.User.Identity != null &&
  9. (context.User?.FindFirst(ClaimTypes.Email).Value != "cc@tnblog.com")
  10. )
  11. {
  12. context.Succeed(requirement);
  13. }
  14. else
  15. {
  16. context.Fail(new AuthorizationFailureReason(requirement, "授权失败"));
  17. }
  18. return Task.CompletedTask;
  19. }
  20. }
  1. builder.Services.AddAuthorization(options =>
  2. {
  3. options.AddPolicy("Tnblog", policy =>
  4. {
  5. policy.Requirements.Add(new TnblogRequirement());
  6. });
  7. });
  1. [Authorize("Tnblog")]
  2. public async Task SendMessage(string data)
  3. {
  4. Console.WriteLine("Have one Data!");
  5. await Clients.All.SendAll(_common.SendAll(data));
  6. await Clients.Caller.SendAll(_common.SendCaller());
  7. }


接下来我们分别用bob账户和cc账户来进行检测,发现cc账户发送消息失败。

SignalR 中的组


简单来讲,按照客户端连接来进行分组。
ChatHub中,我们可以直接使用Group来进行组的添加和删除,由于它的Groups是不允许外部查看访问的所以我这里自定义的一个类来专门记录。

  1. public static class GroupStore
  2. {
  3. public static Dictionary<string, List<string>> Groups = new Dictionary<string, List<string>>();
  4. public static void Add(string groupname,string Id)
  5. {
  6. if (Groups.ContainsKey(groupname))
  7. {
  8. if (Groups.TryGetValue(groupname,out var values))
  9. {
  10. if (values.Contains(Id))
  11. return;
  12. values.Add(Id);
  13. }
  14. else
  15. {
  16. throw new Exception("Add group Error");
  17. }
  18. }
  19. else
  20. {
  21. var newvalues = new List<string>() { Id };
  22. Groups.Add(groupname, newvalues);
  23. }
  24. }
  25. public static void Remove(string groupname, string Id)
  26. {
  27. if (Groups.ContainsKey(groupname))
  28. {
  29. if (Groups.TryGetValue(groupname, out var values))
  30. {
  31. if (!values.Contains(Id))
  32. return;
  33. values.Remove(Id);
  34. if (!(values.Count > 0))
  35. Groups.Remove(groupname);
  36. }
  37. else
  38. {
  39. throw new Exception("Remove group Error");
  40. }
  41. }
  42. }
  43. /// <summary>
  44. /// 连接断开时删除
  45. /// </summary>
  46. /// <param name="Id"></param>
  47. public static void UnConnection(string Id)
  48. {
  49. Groups.Where(x=>x.Value.Contains(Id)).AsParallel().ForAll(x => x.Value.Remove(Id));
  50. }
  51. }


ChatHub中,添加可以调用组的方法

  1. public override Task OnDisconnectedAsync(Exception exception)
  2. {
  3. var id = Context.ConnectionId;
  4. UserIdsStore.Ids.Remove(id);
  5. _logger.LogInformation($"Client ConnectionId=> [[{id}]] Already Close Connection Server!");
  6. // 删除相关组下的信息
  7. GroupStore.UnConnection(Context.ConnectionId);
  8. return base.OnDisconnectedAsync(exception);
  9. }
  10. /// <summary>
  11. /// 添加组
  12. /// </summary>
  13. /// <param name="groupName"></param>
  14. /// <returns></returns>
  15. public async Task AddToGroup(string groupName)
  16. {
  17. await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
  18. // 按照组来发送消息
  19. await Clients.Group(groupName).SendCustomUserMessage($"{Context.ConnectionId} has joined the group {groupName}.");
  20. GroupStore.Add(groupName, Context.ConnectionId);
  21. }
  22. /// <summary>
  23. /// 删除组
  24. /// </summary>
  25. /// <param name="groupName"></param>
  26. /// <returns></returns>
  27. public async Task RemoveFromGroup(string groupName)
  28. {
  29. await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
  30. // 按照组来发送消息
  31. await Clients.Group(groupName).SendCustomUserMessage($"{Context.ConnectionId} has left the group {groupName}.");
  32. GroupStore.Remove(groupName, Context.ConnectionId);
  33. }


在Vue客户端中,我们添加组和离开组的一些内容。

  1. <div>
  2. Group: <input type="text" v-model="group" >
  3. <button @click="onAddGroupButton" >加入组</button>
  4. <button @click="onRemoveGroupButton" >离开组</button>
  5. </div>
  6. ...
  7. onAddGroupButton() {
  8. var e = this
  9. signal
  10. .invoke('AddToGroup', e.group)
  11. .catch(function(err) {return console.error(err) })
  12. },
  13. onRemoveGroupButton() {
  14. var e = this
  15. signal
  16. .invoke('RemoveFromGroup', e.group)
  17. .catch(function(err) {return console.error(err) })
  18. },


断开第二个页面的链接(刷新一下),再次查看。


我们让bob用户离开aa组,再次查看。

使用实体发送消息


首先在后端定义我们的实体和服务器上的接口。

  1. public class TestModel
  2. {
  3. public string Message { get; set; }
  4. public string Email { get; set; }
  5. }
  1. /// <summary>
  2. /// 发送实体模型 ChatHub类中
  3. /// </summary>
  4. /// <param name="data"></param>
  5. /// <returns></returns>
  6. public async Task SendTestModelMessage(TestModel data)
  7. {
  8. await Clients.All.SendTestModel(data);
  9. await Clients.Caller.SendAll(_common.SendCaller());
  10. }
  1. public interface IChatClient
  2. {
  3. Task SendTestModel(TestModel model);
  4. }


更改客户端,在客户端中添加相关接口的发送与接收。

  1. <div>
  2. Message: <input type="text" v-model="message1" >
  3. Email: <input type="text" v-model="email" >
  4. <button @click="onModelClickButton" >发送实体</button>
  5. </div>
  6. ...
  7. message1: "",
  8. email: "",
  9. ...
  10. onModelClickButton() {
  11. var e = this
  12. signal
  13. .invoke('SendTestModelMessage',{ message: e.message1, email: e.email })
  14. .catch(function(err) {return console.error(err) })
  15. }

最后建议在发送消息的时候使用:ConfigureAwait异步发送避免卡死现象。


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

一点flex布局的运用

水平分割:html:&lt;divclass=&quot;flex-container&quot;&gt; &lt;divclass=&quot;flex-item&quot;&gt;flexitem1&lt;/div...

c 锁的运用

锁的一般分类:乐观锁、悲观锁、共享锁、排它锁、互斥锁锁的对象为什么要是私有的只读的因为防止被修改C#中Monitor和Lock以...

.net json序列化匿名类型jobject的运用

在反序列化的时候我们可以不用每次都添加一次实体,如果只是简单临时的用一下可以使用jobject即可解析方法一般有两种方法一...

grpc 在vs中的运用与proto同步

思 考 问 题 grpc 服务器当对 proto 文件进行修改时每次都去 Copy 到本地 Client 是比较麻烦的,有没有快捷的方法解决?Gr...

Weave Scope 的运用

Weave Scope 的运用[TOC] Weave Scope 的介绍 简介 Weave Scope 用于监控、可视化和管理 Docker 以及 Kubernetes...

Kubernetes Velero 备份的运用

Velero 的运用[TOC] Velero简介Velero是一个开源工具,可以安全地备份,恢复和迁移Kubernetes集群和持久卷。它既可以在本...

Windows搭建sshd服务与.net的运用

Windows搭建sshd服务与.net的运用[TOC] 下载win sshd 当前最新包连接: https://github.com/PowerShell/Win32-OpenSSH/re...

.net6 Signalr+Vue3 的运用

.net6 Signalr+Vue3 的运用(上)[TOC] 什么是 SignalR?ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加...

.net6 Signalr+Vue3 配合Ingress Nginx的运用

.net6 Signalr+Vue3 配合Ingress Nginx的运用[TOC] 结合上篇:https://www.tnblog.net/hb/article/details/7963 项目打...

C ?、?? 问号和2个问号的用法类型?、对象?

C# ?C# ???:单问号1.定义数据类型可为空。可用于对int,double,bool等无法直接赋值为null的数据类型进行null的赋值如这...

Python实例 1-日志抓取处理 补错附日志小技巧

有时候数据出了问题,可以从日志中恢复数据(如果你没记日志..没备份..→_→..)一、日志展示介绍个平常自己用的小方法,如...

C 数组拆分泛型

主要用到了泛型。泛型是c#2.0的一个新增加的特性,它为使用c#语言编写面向对象程序增加了极大的效力和灵活性。不会强行对值...

MySQL 视图的增删改 查

要显示视图的定义,需要在SHOWCREATEVIEW子句之后指定视图的名称, 我们先来创建几张表,完事后在进行演示:--用户信息表...

使用NPOI导出excel包括图片

Excl模板导出相信我们都会,那么模板上要导出图片呢?嗯~还是来个例子:准备工作:首先要引用NPOI包:然后获取数据集(我这...

ajaxSubmit异步上传图片嘘,外面都是假的

引用代码&lt;scriptsrc=&quot;/Scripts/jquery.form.js&quot;&gt;&lt;/script&gt;js就在旁边img链接中,只不过大小为0x0,...
这一世以无限游戏为使命!
排名
2
文章
640
粉丝
44
评论
93
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术