我正在尝试使用 SignalR(.NET 6)和 Angular ~13 向特定用户实现直接消息,到目前为止我得到了很多有关实现的信息,但没有一个起作用。
所以在客户端(Angular)我有发送消息的方法
export interface IChatMessage {
id?: number;
message: string;
from?: string;
to?: string;
created?: Date | null;
}
发送到集线器时,我仅发送
message
和 to
,并且 to
是该消息的接收者。
我的chat.service.ts
是
export class ChatService {
private hubConnection: HubConnection;
private receivedMessageSubject = new Subject<IChatMessage>();
public connectionId: string;
receivedMessage$: Observable<IChatMessage> = this.receivedMessageSubject.asObservable();
constructor(private apiService: ApiService) {
this.hubConnection = new HubConnectionBuilder()
.withUrl(environment.apiUrl + API_ROUTES.CHAT, {accessTokenFactory: () => localStorage.getItem('token')})
.build();
this.hubConnection.start().then(() => console.log('connection started')).then(() => this.getConnectionId()).catch((err) => console.error(err));
this.hubConnection.on('ReceiveMessage', (message) => {
console.log('Received message from server:', message);
this.receivedMessageSubject.next(message);
});
}
private getConnectionId = () => {
this.hubConnection.invoke('getconnectionid')
.then((data) => {
this.connectionId = data;
});
}
getMessages(id: string) {
return this.apiService.post(environment.apiUrl + API_ROUTES.CHAT_MESSAGES, {userId: id});
}
sendMessage(message: IChatMessage): void {
this.hubConnection.invoke('SendMessage', message, this.connectionId)
.then((response) => console.log('Server response:', response))
.catch(error => console.error('Error sending message through SignalR:', error));
}
}
在服务器端我有集线器
ChatHub
public async Task SendMessage(ChatMessage message, string connectionId)
{
try
{
var headers = Context.GetHttpContext().Request.Headers;
var user = Context.GetHttpContext().User.Identity;
Console.WriteLine(JsonSerializer.Serialize(Context.ConnectionId));
await Clients.User(connectionId).SendAsync("ReceiveMessage", message);
}
catch (Exception ex)
{
Console.WriteLine($"Error in SendMessage: {ex.Message}");
throw;
}
}
我得到了
connectionId
,但每个用户的情况都不同,无法映射它以连接发送者与接收者。
在Program.cs
我有
using IUserIdProvider = Microsoft.AspNetCore.SignalR.IUserIdProvider;
...
builder.Services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
builder.Services.AddSignalR();
var app = builder.Build();
...
app.UseAuthentication();
...
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/api/chat");
endpoints.MapControllers();
});
我也有
CustomUserIdProvider : IUserIdProvider
public class CustomUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
var uniqueId = connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
return uniqueId;
}
}
但是
uniqueId
中的CustomUserIdProvider
是空的。对于所有控制器,我都有 var user = _userClaims.GetUserId();
,它从 uniqueId
实体的令牌中获取 User
,该实体是 string
并且是唯一的。它有效。如果我这样做 await Clients.All.SendAsync("ReceiveMessage", message);
它会向所有用户发送消息,“connectionId”可用,但仅适用于发送者,因此接收者不会收到此消息。我不知道如何在 .NET 6 + Angular 13 中实现这一点,因为就像我说的,我在过去 5 天里基本上尝试了互联网上可用的所有内容,但仍然没有运气。有谁知道如何实现这一点,以便可以使用接收者的 uniqueId identyfier 以一对一的方式发送它?
当用户(
browser
或other devices
)连接到Hub端点时,生成的connectionId
是由signalr
本身随机生成的,如果应用程序停止/崩溃,connectionIds
将消失,我们可以别再用了。
一个用户只有一个uniqueId,但是他可以打开很多网页,和同事使用多个移动设备,所以我们需要自己维护他们之间的关系。想要了解更多详情,你可以在这个帖子里查看我的回答。
这是我用于测试的示例。
namespace SignalRMiddleawre.Hubs
{
/// <summary>
/// </summary>
[Authorize]
public partial class MainHub : Hub
{
#region Connection
/// <summary>
/// Manage Connected Users
/// </summary>
private static ConcurrentDictionary<string?, List<string>>? ConnectedUsers = new ConcurrentDictionary<string?, List<string>>();
/// <summary>
/// OnConnect Event
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
///
public override async Task OnConnectedAsync()
{
// Get HttpContext In asp.net core signalr
//IHttpContextFeature hcf = (IHttpContextFeature)this.Context.Features[typeof(IHttpContextFeature)];
//HttpContext hc = hcf.HttpContext;
//string uid = hc.Request.Path.Value.Split(new string[] { "=", "" }, StringSplitOptions.RemoveEmptyEntries)[1].ToString();
string? userid = Context.User?.Identity?.Name;
if (userid == null || userid.Equals(string.Empty))
{
Trace.TraceInformation("user not loged in, can't connect signalr service");
return;
}
Trace.TraceInformation(userid + "connected");
// save connection
List<string>? existUserConnectionIds;
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
if (existUserConnectionIds == null)
{
existUserConnectionIds = new List<string>();
}
existUserConnectionIds.Add(Context.ConnectionId);
ConnectedUsers.TryAdd(userid, existUserConnectionIds);
await Clients.All.SendAsync("ServerInfo", userid, userid + " connected, connectionId = " + Context.ConnectionId);
await base.OnConnectedAsync();
}
/// <summary>
/// OnDisconnected event
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public override async Task OnDisconnectedAsync(Exception? exception)
{
string? userid = Context.User?.Identity?.Name;
// save connection
List<string>? existUserConnectionIds;
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
existUserConnectionIds.Remove(Context.ConnectionId);
if (existUserConnectionIds.Count == 0)
{
List<string> garbage;
ConnectedUsers.TryRemove(userid, out garbage);
}
await base.OnDisconnectedAsync(exception);
}
#endregion
#region Message
/// <summary>
/// Send msg to all user
/// </summary>
/// <param name="userid"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessage(string msgType, string message)
{
await Clients.All.SendAsync("ReceiveMessage", msgType, message);
}
/// <summary>
/// Send msg to user by userid
/// </summary>
/// <param name="connectionId"></param>
/// <param name="message">message format : type-message </param>
/// <returns></returns>
public async Task SendToSingleUser(string userid, string message)
{
List<string>? existUserConnectionIds;
// find all the connectionids by userid
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
if (existUserConnectionIds == null)
{
existUserConnectionIds = new List<string>();
}
existUserConnectionIds.Add(Context.ConnectionId);
ConnectedUsers.TryAdd(userid, existUserConnectionIds);
await Clients.Clients(existUserConnectionIds).SendAsync("ReceiveMessage", message);
}
#endregion
}
}