如何从 Xamarin 获取用户 ID 以通过 SignalR.Core 3.0 发送私人消息
How to get the UserID to send a private message through SignalR.Core 3.0 from Xamarin
我正在尝试在 Xamarin 中创建一个发送私人消息的聊天。现在我已经能够向特定组或所有用户发送消息,但我不知道如何从客户端获取用户 ID 以发送私人消息。
我试过 Microsoft 文档,但它只显示了如何在服务器端进行配置,但没有说明如何在客户端进行配置的示例。
我已尝试使用此文档:
https://docs.microsoft.com/en-us/aspnet/core/signalr/groups?view=aspnetcore-2.1
但是我找不到文档或示例来说明如何从客户端找到用户 ID 以便能够发送消息,或者我可能对如何解决问题有一个不好的认识。
服务器端:
public class ChatHub : Hub
{
public async Task JoinChat(string user)
{
await Clients.All.SendAsync("JoinChat", user);
}
public async Task LeaveChat(string user)
{
await Clients.All.SendAsync("LeaveChat", user);
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public void BroadcastMessage(string name, string message)
{
Clients.All.SendAsync("broadcastMessage", name, message);
}
public void Echo(string name, string message)
{
Clients.Client(Context.ConnectionId).SendAsync("echo", name, message + " (echo from server)");
}
public async Task AddToGroup(string groupName, string user)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Entered", user);
//await Clients.Group(groupName).SendAsync("Entered", "_SYSTEM_", $"{user} joined {groupName} with connectionId {Context.ConnectionId}");
}
public async Task RemoveFromGroup(string groupName, string user)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
//await Clients.Client(Context.ConnectionId).SendAsync("Left", "_SYSTEM_", $"{user} leaved {groupName}");
await Clients.Group(groupName).SendAsync("Left", user);
//await Clients.Group(groupName).SendAsync("Left", "_SYSTEM_", $"{user} leaved {groupName}");
}
public async Task SendMessageGroup(string groupName, string user, string message)
{
await Clients.Group(groupName).SendAsync("ReceiveMessage", user, message);
}
//**//
public void SendGroups(string name, IReadOnlyList<string> groups, string message)
{
Clients.Groups(groups).SendAsync("echo", name, message);
}
public void SendGroupExcept(string name, string groupName, IReadOnlyList<string> connectionIdExcept, string message)
{
Clients.GroupExcept(groupName, connectionIdExcept).SendAsync("echo", name, message);
}
public void SendUser(string name, string userId, string message)
{
Clients.User(userId).SendAsync("echo", name, message);
}
public void SendUsers(string name, IReadOnlyList<string> userIds, string message)
{
Clients.Users(userIds).SendAsync("echo", name, message);
}
}
public class CustomUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
// return connection.User?.Identity?.Name;
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:5002");
}));
services.AddSignalR();
services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
}
客户端:
摘自 James Montemagno 来自:
https://github.com/jamesmontemagno/XamChat
public class ChatService
{
public event EventHandler<MessageEventArgs> OnReceivedMessage;
public event EventHandler<MessageEventArgs> OnEnteredOrExited;
public event EventHandler<MessageEventArgs> OnConnectionClosed;
HubConnection hubConnection;
Random random;
bool IsConnected { get; set; }
Dictionary<string, string> ActiveChannels { get; } = new Dictionary<string, string>();
public void Init(string urlRoot, bool useHttps)
{
random = new Random();
var port = (urlRoot == "localhost" || urlRoot == "10.0.2.2") ?
(useHttps ? ":5001" : ":5000") :
string.Empty;
var url = $"http{(useHttps ? "s" : string.Empty)}://{urlRoot}{port}/hubs/chat";
hubConnection = new HubConnectionBuilder()
.WithUrl(url)
.Build();
hubConnection.Closed += async (error) =>
{
OnConnectionClosed?.Invoke(this, new MessageEventArgs("Connection closed...", string.Empty));
IsConnected = false;
await Task.Delay(random.Next(0, 5) * 1000);
try
{
await ConnectAsync();
}
catch (Exception ex)
{
// Exception!
Debug.WriteLine(ex);
}
};
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
OnReceivedMessage?.Invoke(this, new MessageEventArgs(message, user));
});
hubConnection.On<string>("Entered", (user) =>
{
OnEnteredOrExited?.Invoke(this, new MessageEventArgs($"{user} entered.", user));
});
hubConnection.On<string>("Left", (user) =>
{
OnEnteredOrExited?.Invoke(this, new MessageEventArgs($"{user} left.", user));
});
hubConnection.On<string>("EnteredOrLeft", (message) =>
{
OnEnteredOrExited?.Invoke(this, new MessageEventArgs(message, message));
});
}
public async Task ConnectAsync()
{
if (IsConnected)
return;
await hubConnection.StartAsync();
IsConnected = true;
}
public async Task DisconnectAsync()
{
if (!IsConnected)
return;
try
{
await hubConnection.DisposeAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
ActiveChannels.Clear();
IsConnected = false;
}
public async Task LeaveChannelAsync(string group, string userName)
{
if (!IsConnected || !ActiveChannels.ContainsKey(group))
return;
await hubConnection.SendAsync("RemoveFromGroup", group, userName);
ActiveChannels.Remove(group);
}
public async Task JoinChannelAsync(string group, string userName)
{
if (!IsConnected || ActiveChannels.ContainsKey(group))
return;
await hubConnection.SendAsync("AddToGroup", group, userName);
ActiveChannels.Add(group, userName);
}
public async Task SendMessageAsync(string group, string userName, string message)
{
if (!IsConnected)
throw new InvalidOperationException("Not connected");
await hubConnection.InvokeAsync("SendMessageGroup",
group,
userName,
message);
}
public async Task SendMessageUserAsync(string group, string userName, string message)
{
if (!IsConnected)
throw new InvalidOperationException("Not connected");
await hubConnection.InvokeAsync("SendUser",
userName,
"10",
message);
}
}
您缺少的是将连接映射到用户。例如,我会创建一个动态用户列表,并在用户每次连接时映射它们。例如,有一个方法 return a connectionId:
public string GetConnectionId()
{
return this.Context.ConnectionId;
}
收到 connectionId 后,您可以调用一个方法将用户映射到连接。因此,如果您需要向特定用户发送消息,您可以在列表中搜索该用户,并获取该特定用户的 connectionId,这样您就可以向他发送消息,例如:
Clients.Client(connectionId).SendAsync(from, to, message);
你可以阅读 this documentation 关于这个问题,虽然是针对旧的 SignalR,你可以使用相同的逻辑并根据你的需要进行调整。
我正在尝试在 Xamarin 中创建一个发送私人消息的聊天。现在我已经能够向特定组或所有用户发送消息,但我不知道如何从客户端获取用户 ID 以发送私人消息。
我试过 Microsoft 文档,但它只显示了如何在服务器端进行配置,但没有说明如何在客户端进行配置的示例。
我已尝试使用此文档: https://docs.microsoft.com/en-us/aspnet/core/signalr/groups?view=aspnetcore-2.1
但是我找不到文档或示例来说明如何从客户端找到用户 ID 以便能够发送消息,或者我可能对如何解决问题有一个不好的认识。
服务器端:
public class ChatHub : Hub
{
public async Task JoinChat(string user)
{
await Clients.All.SendAsync("JoinChat", user);
}
public async Task LeaveChat(string user)
{
await Clients.All.SendAsync("LeaveChat", user);
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public void BroadcastMessage(string name, string message)
{
Clients.All.SendAsync("broadcastMessage", name, message);
}
public void Echo(string name, string message)
{
Clients.Client(Context.ConnectionId).SendAsync("echo", name, message + " (echo from server)");
}
public async Task AddToGroup(string groupName, string user)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Entered", user);
//await Clients.Group(groupName).SendAsync("Entered", "_SYSTEM_", $"{user} joined {groupName} with connectionId {Context.ConnectionId}");
}
public async Task RemoveFromGroup(string groupName, string user)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
//await Clients.Client(Context.ConnectionId).SendAsync("Left", "_SYSTEM_", $"{user} leaved {groupName}");
await Clients.Group(groupName).SendAsync("Left", user);
//await Clients.Group(groupName).SendAsync("Left", "_SYSTEM_", $"{user} leaved {groupName}");
}
public async Task SendMessageGroup(string groupName, string user, string message)
{
await Clients.Group(groupName).SendAsync("ReceiveMessage", user, message);
}
//**//
public void SendGroups(string name, IReadOnlyList<string> groups, string message)
{
Clients.Groups(groups).SendAsync("echo", name, message);
}
public void SendGroupExcept(string name, string groupName, IReadOnlyList<string> connectionIdExcept, string message)
{
Clients.GroupExcept(groupName, connectionIdExcept).SendAsync("echo", name, message);
}
public void SendUser(string name, string userId, string message)
{
Clients.User(userId).SendAsync("echo", name, message);
}
public void SendUsers(string name, IReadOnlyList<string> userIds, string message)
{
Clients.Users(userIds).SendAsync("echo", name, message);
}
}
public class CustomUserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
// return connection.User?.Identity?.Name;
return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:5002");
}));
services.AddSignalR();
services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
}
客户端:
摘自 James Montemagno 来自: https://github.com/jamesmontemagno/XamChat
public class ChatService
{
public event EventHandler<MessageEventArgs> OnReceivedMessage;
public event EventHandler<MessageEventArgs> OnEnteredOrExited;
public event EventHandler<MessageEventArgs> OnConnectionClosed;
HubConnection hubConnection;
Random random;
bool IsConnected { get; set; }
Dictionary<string, string> ActiveChannels { get; } = new Dictionary<string, string>();
public void Init(string urlRoot, bool useHttps)
{
random = new Random();
var port = (urlRoot == "localhost" || urlRoot == "10.0.2.2") ?
(useHttps ? ":5001" : ":5000") :
string.Empty;
var url = $"http{(useHttps ? "s" : string.Empty)}://{urlRoot}{port}/hubs/chat";
hubConnection = new HubConnectionBuilder()
.WithUrl(url)
.Build();
hubConnection.Closed += async (error) =>
{
OnConnectionClosed?.Invoke(this, new MessageEventArgs("Connection closed...", string.Empty));
IsConnected = false;
await Task.Delay(random.Next(0, 5) * 1000);
try
{
await ConnectAsync();
}
catch (Exception ex)
{
// Exception!
Debug.WriteLine(ex);
}
};
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
OnReceivedMessage?.Invoke(this, new MessageEventArgs(message, user));
});
hubConnection.On<string>("Entered", (user) =>
{
OnEnteredOrExited?.Invoke(this, new MessageEventArgs($"{user} entered.", user));
});
hubConnection.On<string>("Left", (user) =>
{
OnEnteredOrExited?.Invoke(this, new MessageEventArgs($"{user} left.", user));
});
hubConnection.On<string>("EnteredOrLeft", (message) =>
{
OnEnteredOrExited?.Invoke(this, new MessageEventArgs(message, message));
});
}
public async Task ConnectAsync()
{
if (IsConnected)
return;
await hubConnection.StartAsync();
IsConnected = true;
}
public async Task DisconnectAsync()
{
if (!IsConnected)
return;
try
{
await hubConnection.DisposeAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
ActiveChannels.Clear();
IsConnected = false;
}
public async Task LeaveChannelAsync(string group, string userName)
{
if (!IsConnected || !ActiveChannels.ContainsKey(group))
return;
await hubConnection.SendAsync("RemoveFromGroup", group, userName);
ActiveChannels.Remove(group);
}
public async Task JoinChannelAsync(string group, string userName)
{
if (!IsConnected || ActiveChannels.ContainsKey(group))
return;
await hubConnection.SendAsync("AddToGroup", group, userName);
ActiveChannels.Add(group, userName);
}
public async Task SendMessageAsync(string group, string userName, string message)
{
if (!IsConnected)
throw new InvalidOperationException("Not connected");
await hubConnection.InvokeAsync("SendMessageGroup",
group,
userName,
message);
}
public async Task SendMessageUserAsync(string group, string userName, string message)
{
if (!IsConnected)
throw new InvalidOperationException("Not connected");
await hubConnection.InvokeAsync("SendUser",
userName,
"10",
message);
}
}
您缺少的是将连接映射到用户。例如,我会创建一个动态用户列表,并在用户每次连接时映射它们。例如,有一个方法 return a connectionId:
public string GetConnectionId()
{
return this.Context.ConnectionId;
}
收到 connectionId 后,您可以调用一个方法将用户映射到连接。因此,如果您需要向特定用户发送消息,您可以在列表中搜索该用户,并获取该特定用户的 connectionId,这样您就可以向他发送消息,例如:
Clients.Client(connectionId).SendAsync(from, to, message);
你可以阅读 this documentation 关于这个问题,虽然是针对旧的 SignalR,你可以使用相同的逻辑并根据你的需要进行调整。