.net SignalR在Angular 中向单个用户直接发送消息

nkcskrwz  于 2023-11-20  发布在  .NET
关注(0)|答案(2)|浏览(154)

我试图使用SignalR(.NET 6)和Angular ~13实现直接消息到特定用户,到目前为止,我得到了很多关于实现的信息,但没有一个工作。
所以在客户端(Angular)我有方法发送消息

  1. export interface IChatMessage {
  2. id?: number;
  3. message: string;
  4. from?: string;
  5. to?: string;
  6. created?: Date | null;
  7. }

字符串
其中发送到hub我只发送messagetoto是该消息的接收者。我的chat.service.ts

  1. export class ChatService {
  2. private hubConnection: HubConnection;
  3. private receivedMessageSubject = new Subject<IChatMessage>();
  4. public connectionId: string;
  5. receivedMessage$: Observable<IChatMessage> = this.receivedMessageSubject.asObservable();
  6. constructor(private apiService: ApiService) {
  7. this.hubConnection = new HubConnectionBuilder()
  8. .withUrl(environment.apiUrl + API_ROUTES.CHAT, {accessTokenFactory: () => localStorage.getItem('token')})
  9. .build();
  10. this.hubConnection.start().then(() => console.log('connection started')).then(() => this.getConnectionId()).catch((err) => console.error(err));
  11. this.hubConnection.on('ReceiveMessage', (message) => {
  12. console.log('Received message from server:', message);
  13. this.receivedMessageSubject.next(message);
  14. });
  15. }
  16. private getConnectionId = () => {
  17. this.hubConnection.invoke('getconnectionid')
  18. .then((data) => {
  19. this.connectionId = data;
  20. });
  21. }
  22. getMessages(id: string) {
  23. return this.apiService.post(environment.apiUrl + API_ROUTES.CHAT_MESSAGES, {userId: id});
  24. }
  25. sendMessage(message: IChatMessage): void {
  26. this.hubConnection.invoke('SendMessage', message, this.connectionId)
  27. .then((response) => console.log('Server response:', response))
  28. .catch(error => console.error('Error sending message through SignalR:', error));
  29. }
  30. }


在服务器端,我有集线器ChatHub

  1. public async Task SendMessage(ChatMessage message, string connectionId)
  2. {
  3. try
  4. {
  5. var headers = Context.GetHttpContext().Request.Headers;
  6. var user = Context.GetHttpContext().User.Identity;
  7. Console.WriteLine(JsonSerializer.Serialize(Context.ConnectionId));
  8. await Clients.User(connectionId).SendAsync("ReceiveMessage", message);
  9. }
  10. catch (Exception ex)
  11. {
  12. Console.WriteLine($"Error in SendMessage: {ex.Message}");
  13. throw;
  14. }
  15. }


我得到了connectionId,但它对每个用户都是不同的,不能Map它来连接发送者和接收者。

  1. using IUserIdProvider = Microsoft.AspNetCore.SignalR.IUserIdProvider;
  2. ...
  3. builder.Services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
  4. builder.Services.AddSignalR();
  5. var app = builder.Build();
  6. ...
  7. app.UseAuthentication();
  8. ...
  9. app.UseEndpoints(endpoints =>
  10. {
  11. endpoints.MapHub<ChatHub>("/api/chat");
  12. endpoints.MapControllers();
  13. });


还有CustomUserIdProvider : IUserIdProvider

  1. public class CustomUserIdProvider : IUserIdProvider
  2. {
  3. public string GetUserId(HubConnectionContext connection)
  4. {
  5. var uniqueId = connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
  6. return uniqueId;
  7. }
  8. }


但是CustomUserIdProvider中的uniqueId是空的。对于所有控制器,我有var user = _userClaims.GetUserId();,它从User实体的令牌中获取uniqueId,该实体是string并且是唯一的。它可以工作。如果我做await Clients.All.SendAsync("ReceiveMessage", message);,它会向所有用户发送消息,'connectionId'是可用的,但只有发送方,所以接收方不会收到此消息。我不知道如何在.NET 6 +中实现这一点Angular 13,因为就像我说的,我已经尝试了过去5天基本上一切都可以在互联网上,但仍然没有运气。有没有人有任何想法如何实现这一点,所以它可以在1对1的方式发送使用接收方的uniqueId标识符?

mutmk8jj

mutmk8jj1#

终于解决了这个问题。
Program.cs中在AddAuthentication中在AddJwtBearer中(因为身份验证是使用JWT),我添加了

  1. config.Events = new JwtBearerEvents
  2. {
  3. OnMessageReceived = context =>
  4. {
  5. var accessToken = context.Request.Query["access_token"];
  6. var path = context.HttpContext.Request.Path;
  7. if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/api/chat")))
  8. {
  9. context.Token = accessToken;
  10. }
  11. return Task.CompletedTask;
  12. }
  13. };

字符串
下一个添加builder.Services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();,在Useauthorization之后添加

  1. var idProvider = new CustomUserIdProvider();
  2. GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);


我还创建了CustomUserIdPrvider

  1. public class CustomUserIdProvider : IUserIdProvider
  2. {
  3. public string GetUserId(HubConnectionContext connection)
  4. {
  5. var uniqueId = connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
  6. return uniqueId;
  7. }
  8. }


SendMessage in聊天中心`

  1. public async Task SendMessage(ChatMessage message)
  2. {
  3. var httpContext = Context.GetHttpContext();
  4. var user = httpContext.User.Claims.Where(user => user.Type == ClaimTypes.NameIdentifier).FirstOrDefault()?.Value;
  5. var token = httpContext.Request.Query["access_token"];
  6. message.From = user;
  7. message.Created = DateTime.Now.ToLocalTime();
  8. await Clients.Users(message.To, user).SendAsync("ReceiveMessage", message);
  9. }


因此,使用Clients.Users(message.To, user),我可以使用uniqueId(NOT!connectionId!)将消息内容发送给特定用户

展开查看全部
jhiyze9q

jhiyze9q2#

当用户(browserother devices)连接到Hub端点时,生成的connectionId是由signalr本身随机生成的,如果应用程序停止/崩溃,connectionIds将消失,我们不能再使用它。
一个用户只有一个uniqueId,但是他可以打开很多网页,可以和同事一起使用多个移动的设备,所以我们需要自己维护他们之间的关系,更多细节,you can check my answer in this thread

这是我用于测试的样品。

  1. namespace SignalRMiddleawre.Hubs
  2. {
  3. /// <summary>
  4. /// </summary>
  5. [Authorize]
  6. public partial class MainHub : Hub
  7. {
  8. #region Connection
  9. /// <summary>
  10. /// Manage Connected Users
  11. /// </summary>
  12. private static ConcurrentDictionary<string?, List<string>>? ConnectedUsers = new ConcurrentDictionary<string?, List<string>>();
  13. /// <summary>
  14. /// OnConnect Event
  15. /// </summary>
  16. /// <param name="userid"></param>
  17. /// <returns></returns>
  18. ///
  19. public override async Task OnConnectedAsync()
  20. {
  21. // Get HttpContext In asp.net core signalr
  22. //IHttpContextFeature hcf = (IHttpContextFeature)this.Context.Features[typeof(IHttpContextFeature)];
  23. //HttpContext hc = hcf.HttpContext;
  24. //string uid = hc.Request.Path.Value.Split(new string[] { "=", "" }, StringSplitOptions.RemoveEmptyEntries)[1].ToString();
  25. string? userid = Context.User?.Identity?.Name;
  26. if (userid == null || userid.Equals(string.Empty))
  27. {
  28. Trace.TraceInformation("user not loged in, can't connect signalr service");
  29. return;
  30. }
  31. Trace.TraceInformation(userid + "connected");
  32. // save connection
  33. List<string>? existUserConnectionIds;
  34. ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
  35. if (existUserConnectionIds == null)
  36. {
  37. existUserConnectionIds = new List<string>();
  38. }
  39. existUserConnectionIds.Add(Context.ConnectionId);
  40. ConnectedUsers.TryAdd(userid, existUserConnectionIds);
  41. await Clients.All.SendAsync("ServerInfo", userid, userid + " connected, connectionId = " + Context.ConnectionId);
  42. await base.OnConnectedAsync();
  43. }
  44. /// <summary>
  45. /// OnDisconnected event
  46. /// </summary>
  47. /// <param name="userid"></param>
  48. /// <returns></returns>
  49. public override async Task OnDisconnectedAsync(Exception? exception)
  50. {
  51. string? userid = Context.User?.Identity?.Name;
  52. // save connection
  53. List<string>? existUserConnectionIds;
  54. ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
  55. existUserConnectionIds.Remove(Context.ConnectionId);
  56. if (existUserConnectionIds.Count == 0)
  57. {
  58. List<string> garbage;
  59. ConnectedUsers.TryRemove(userid, out garbage);
  60. }
  61. await base.OnDisconnectedAsync(exception);
  62. }
  63. #endregion
  64. #region Message
  65. /// <summary>
  66. /// Send msg to all user
  67. /// </summary>
  68. /// <param name="userid"></param>
  69. /// <param name="message"></param>
  70. /// <returns></returns>
  71. public async Task SendMessage(string msgType, string message)
  72. {
  73. await Clients.All.SendAsync("ReceiveMessage", msgType, message);
  74. }
  75. /// <summary>
  76. /// Send msg to user by userid
  77. /// </summary>
  78. /// <param name="connectionId"></param>
  79. /// <param name="message">message format : type-message </param>
  80. /// <returns></returns>
  81. public async Task SendToSingleUser(string userid, string message)
  82. {
  83. List<string>? existUserConnectionIds;
  84. // find all the connectionids by userid
  85. ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
  86. if (existUserConnectionIds == null)
  87. {
  88. existUserConnectionIds = new List<string>();
  89. }
  90. existUserConnectionIds.Add(Context.ConnectionId);
  91. ConnectedUsers.TryAdd(userid, existUserConnectionIds);
  92. await Clients.Clients(existUserConnectionIds).SendAsync("ReceiveMessage", message);
  93. }
  94. #endregion
  95. }
  96. }

字符串

展开查看全部

相关问题