SignalR core receive messages from specific user - c#

SignalR core is very new, so the docs detail how to use about it are very rare.
I've done the tutorial from Microsoft and successfully sent messages to all the clients. Now I want to send for specific user, with
public Task SendPrivateMessage(string user, string message, string to)
{
return Clients.User(to).SendAsync("ReceiveMessage", user, message);
}
the "to" value is the ConnectionID I got from the
public override Task OnConnectedAsync()
{
Console.WriteLine("New ID Connected: " + Context.ConnectionId);
return base.OnConnectedAsync();
}
Here is my client:
public async void InitSignalRAsync()
{
ChatMessage mess = new ChatMessage();
hubConnection = new HubConnectionBuilder().WithUrl("http://localhost:5000/chatHub").Build();
await hubConnection.StartAsync();
hubConnection.On<string, string>("ReceiveMessage", async (user, message) =>
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
mess.user = user;
mess.message = message;
Messages.Add(mess);
});
});
}
private void Send_Click(object sender, RoutedEventArgs e)
{
hubConnection.InvokeAsync("SendPrivateMessage", User.Text, Text.Text, To.Text);
}
The console logs nothing error so I guess it did send but why can't I receive it?

For send to client by client's connectionId you should use of Clients.Client(coonectionId)
,Clients.User() is for send to uniqe user by user's Id.to do send message by user id you can try as follows:
-create a CustomUserIdProvider:
public class CustomUserIdProvider: IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
//get current user id by httpcontext
}
}
and then in startup.cs:
services.AddSignalR();
services.AddSignalRCore();
services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
now in your hub you can send message by user id:
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
for more info go to this link

Related

SignalrR HubConnection disregards query parameter

I want to allow my users connect to a specific group in my SignalR hub, to do this i generate a unique id that the users in that group can share with others. Once a user connects to the hub, the id is generated. On the "connected" event their URL updates with the unique id. When I then use the URL to join the newly created room it seems like two negotiation requests are sent, both containing the the group id as well as the users connection id, yet sometimes(not always) I get a response from the Hub containing a newly generated Group.
Is the ?pod parameter I pass into the url not always assigned before the request is made?
To me it seems completely random, but it's most likely some error I've made in my connection code since I'm relatively new to Angular.
This request happened correctly and I joined the room I wanted.
Correct behavior
This one happened incorrectly and a new room was generated even though, seemingly(?), the request looks the same, save for the web socket connection containing the "pid".
Incorrect behavior
Any help is greatly appreciated!
The code for the Home component where the connection is initiated
export class HomeComponent implements OnInit, OnDestroy{
welcomeMessage:string;
podId:string;
users:User[];
constructor(private signalrService: SignalRService, private http: HttpClient, private activeRoute: ActivatedRoute,
private router: Router, private location: Location) {}
ngOnInit() {
this.activeRoute.queryParams.subscribe(params => {
this.podId = params['pod'];
this.connectToPod();
});
}
ngOnDestroy(): void {
}
connectToPod(){
this.signalrService.startConnection(this.podId);
this.signalrService.addPodConnectedLisener((data: Pod) => {
this.podId = data.id;
this.users = data.users;
this.location.replaceState('/?pod=' + this.podId)
this.welcomeMessage = window.location.origin + '/?pod=' + this.podId;
});
}
}
The code for the SignalR service
export class SignalRService {
private hubConnection: signalR.HubConnection;
private baseUrl = environment.apiUrl;
constructor() { }
public startConnection (podId?: string) {
let idToSend = podId == undefined ? '' : '?pid=' + podId;
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.baseUrl + '/pod' + idToSend)
.build();
this.hubConnection
.start()
.then(() => console.log('Connection started'))
.catch(err => console.log('Error while starting connection: ' + err));
}
public addPodConnectedLisener (connectedCallback: Function) {
return this.hubConnection.on('connected', data => {
connectedCallback(data);
});
}
}
The code for the SignalR Hub
public class PodHub : Hub
{
private readonly IPodConnectionManger _podConnectionManager;
public PodHub(IPodConnectionManger podConnectionManager)
{
_podConnectionManager = podConnectionManager;
}
public override async Task OnConnectedAsync()
{
var podId = Context.GetHttpContext().Request.Query["pid"].ToString();
if (string.IsNullOrEmpty(podId))
{
await CreatePod();
}
else
{
await JoinPod(podId);
}
}
private async Task CreatePod()
{
var newPodId = await _podConnectionManager.AddPod();
var podToSend = await _podConnectionManager.GetPod(newPodId);
await podToSend.AddUser(Context.ConnectionId);
await Groups.AddToGroupAsync(Context.ConnectionId, podToSend.Id);
await Clients.Group(podToSend.Id).SendAsync("connected", podToSend);
}
private async Task JoinPod(string id)
{
var podToJoin = await _podConnectionManager.GetPod(id);
await podToJoin.AddUser(Context.ConnectionId);
await Groups.AddToGroupAsync(Context.ConnectionId, podToJoin.Id);
await Clients.Group(podToJoin.Id).SendAsync("connected", podToJoin);
}
}

C# SignalR2 receive online user list from server

I have created a chat using SignalR2. The client and server itself works fine. Now, I'm trying to implement a 'users online' function. The server code seems about right, but I'm struggling to make the client receive the data that the server pushes back to the client.
This is the server code below:
public static List<string> Users = new List<string>();
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
Clients.All.addMessage(name, message);
}
public void SendUserList(List<string> users)
{
var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>();
context.Clients.All.updateUserList(users);
}
public override Task OnConnected()
{
string clientId = GetClientId();
//if (Users.IndexOf(clientId) == -1)
//{
Users.Add(clientId);
//}
SendCount(Users.Count);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
System.Diagnostics.Debug.WriteLine("Disconnected");
SendCount(Users.Count);
return base.OnDisconnected(stopCalled);
}
private string GetClientId()
{
string clientId = "";
if (Context.QueryString["clientId"] != null)
{
// clientId passed from application
clientId = this.Context.QueryString["clientId"];
}
if (string.IsNullOrEmpty(clientId.Trim()))
{
clientId = Context.ConnectionId;
}
return clientId;
}
public void SendCount(int count)
{
// Call the addNewMessageToPage method to update clients.
var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>();
context.Clients.All.updateUsersOnlineCount(count);
}
Below is the client code for connecting / receiving messages:
public static async void ConnectAsync(RadChat ChatInternal)
{
ChatInternal.Author = new Author(null, Varribles.Agent);
var querystringData = new Dictionary<string, string>();
querystringData.Add("clientId", Varribles.Agent);
Connection = new HubConnection(ServerURI, querystringData);
HubProxy = Connection.CreateHubProxy("chatHub");
//Handle incoming event from server: use Invoke to write to console from SignalR's thread
HubProxy.On<string, string>("AddMessage", (name, message) =>
ChatInternal.Invoke((Action)(() =>
Backend.GET.Messages(ChatInternal)
)));
try
{
await Connection.Start();
Backend.GET.Messages(ChatInternal);
}
catch (System.Net.Http.HttpRequestException)
{
//No connection: Don't enable Send button or show chat UI
return;
}
}
Now, my question is, how can I retrieve the 'Users' list from the server?
Thanks in advance

Real-time notification upon registration using SignalR

I want to send real-time notification in ASP.NET Boilerplate. Notification is saving successfully in Abp.NotificationSubscription table on subscription. When I publish the notification, the notification got saved in the Abp.Notification table but it is not displayed to the user in real-time.
My server-side code:
public async Task<RegisterOutput> Register(RegisterInput input)
{
public async Task<RegisterOutput> Register(RegisterInput input)
{
var user = await _userRegistrationManager.RegisterAsync(
input.Name,
input.Surname,
input.EmailAddress,
input.UserName,
input.Password,
true
);
_notificationSubscriptionManager.SubscribeToAllAvailableNotifications(user.ToUserIdentifier());
await _appNotifier.WelcomeToTheApplicationAsync(user);
var notification = _userNotificationManager.GetUserNotifications(user.ToUserIdentifier());
await _realTimeNotifier.SendNotificationsAsync(notification.ToArray());
// ...
}
}
_appNotifier implements WelcomeToTheApplicationAsync(user):
public async Task WelcomeToTheApplicationAsync(User user)
{
await _notificationPublisher.PublishAsync(
AppNotificationName.WelcomeToTheApplication,
new SendNotificationData("Naeem", "Hello I have sended this notification to you"),
severity: NotificationSeverity.Success,
userIds: new[] { user.ToUserIdentifier() }
);
}
SendNotificationData inherits from NotificationData:
public class SendNotificationData : NotificationData
{
public string name { get; set; }
public string message { get; set; }
public SendNotificationData(string _name, string _message)
{
name = _name;
message = _message;
}
}
I want to sent welcome notification to the user when he registers itself by using the register link on the login page. In the CloudbaseLine.Application project ... we have AccountAppService which contain the code for registering the user and sending notification to it after successful registration but the problem is notification go saved in Abp.NotificationSubscriptionTable and UserNotificationTable but user cannot received them.
public async Task<RegisterOutput> Register(RegisterInput input)
{
var user = await _userRegistrationManager.RegisterAsync(
input.Name,
input.Surname,
input.EmailAddress,
input.UserName,
input.Password,
true
);
_notificationSubscriptionManager.SubscribeToAllAvailableNotifications(user.ToUserIdentifier());
await _appNotifier.WelcomeToTheApplicationAsync(user);
var notification = _userNotificationManager.GetUserNotifications(user.ToUserIdentifier());
await _realTimeNotifier.SendNotificationsAsync(notification.ToArray());
// ...
}
public async Task WelcomeToTheApplicationAsync(User user)
{
await _notificationPublisher.PublishAsync(
AppNotificationName.WelcomeToTheApplication,
new SendNotificationData("Naeem", "Hello I have sended this notification to you"),
severity: NotificationSeverity.Success,
userIds: new[] { user.ToUserIdentifier() }
);
}
The user is not connected to the SignalR hub in the Register method.
One way to handle that is to enqueue a background job:
await _backgroundJobManager.EnqueueAsync<WelcomeNotificationJob, UserIdentifier>(
user.ToUserIdentifier(),
delay: TimeSpan.FromSeconds(5)
);
public class WelcomeNotificationJob : BackgroundJob<UserIdentifier>, ITransientDependency
{
private readonly IRealTimeNotifier _realTimeNotifier;
private readonly IUserNotificationManager _userNotificationManager;
public WelcomeNotificationJob(
IRealTimeNotifier realTimeNotifier,
IUserNotificationManager userNotificationManager)
{
_realTimeNotifier = realTimeNotifier;
_userNotificationManager = userNotificationManager;
}
[UnitOfWork]
public override void Execute(UserIdentifier args)
{
var notifications = _userNotificationManager.GetUserNotifications(args);
AsyncHelper.RunSync(() => _realTimeNotifier.SendNotificationsAsync(notifications.ToArray()));
}
}
Don't forget to register data formatters for custom notification data types, on the client side:
abp.notifications.messageFormatters['CloudBaseLine.Notification.SendNotificationData'] = function (userNotification) {
return userNotification.notification.data.message;
}

Joining a group in SignalR

I am developing WPF application where one instance of a program will be able to communicate to other via SignalR Self-Host. Everything is fine, except that I don't know how and where do I join a group so that program would know to which users send notification. Any help would be appreciated.
Here is my sample code:
//client side
private async void ConnectAsync()
{
Connection = new HubConnection(ServerURI);
Connection.Closed += Connection_Closed;
HubProxy = Connection.CreateHubProxy("MyHub");
//Handle incoming event from server: use Invoke to write to console from SignalR's thread
HubProxy.On<string, string>("AddMessage", (name, message) =>
this.Dispatcher.Invoke(() => RichTextBoxConsole.AppendText(String.Format("{0}: {1}\r", name, message))
)
);
try
{
await Connection.Start();
}
catch (HttpRequestException)
{
StatusText.Content = "Unable to connect to server: Start server before connecting clients.";
//No connection: Don't enable Send button or show chat UI
return;
}
HubProxy.Invoke<string>("JoinGroup", "foobar").Wait(); // Do I have to do it here?
}
//serverside
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.Group("foobar").AddMessage(name, message);
}
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public Task AddGroups()
{
return Groups.Add(Context.ConnectionId, "foobar");
}
public override Task OnConnected()
{
return AddGroups();
}
}
I think you have it almost right, but your OnConnected method isn't quite right.
Try changing it to:
public override Task OnConnected()
{
Groups.Add(Context.ConnectionId, "foobar");
return base.OnConnected();
}
This is basically the code I've used, and the difference is the base.OnConnected() part which is my guess as to what's breaking for you.
The problem was that when adding a new Group:
public Task AddGroups()
{
return Groups.Add(Context.ConnectionId, "foobar");
}
it for some reason added prefix "hg-MyHub." to the name of the Group so the name of the Group looked like this: "hg-MyHub.foobar" and of course using this code:
HubProxy.Invoke<string>("JoinGroup", "foobar").Wait();
didn't do anything simply because there was no Group with name "foobar".
Hope this could be usefull for somebody.

How can I get information about the user using a token

There is a need to receive user data using a token. Hello. There is a need to receive user data using a token. I have a web api + websockets, websockets connection via a web browser.
var webSocket = new WebSocket(handlerUrl);
//Open connection handler.
webSocket.onopen = function () {
webSocket.send("{\"type\":\"LOGIN\",\"access_token\":\"Bearer HIDDEN\"}");
};
Once connected, I immediately send token.
On the server side, it looks as follows:
public class SocketClientController: ApiController
{
 public HttpResponseMessage Get ()
 {
HttpContext.Current.AcceptWebSocketRequest (new WebSocketHandler ());
return Request.CreateResponse (HttpStatusCode.SwitchingProtocols);
}
<miss>
Socket class:
<miss>
private void Login(string access_token)
{
// here i want get user info
}
public override void OnMessage(string input)
{
dynamic data = JObject.Parse(input);
switch ((string)data.type)
{
case "LOGIN":
Login((string)data.access_token);
break;
}
}
I use Identity, a variant with a token when you first received from the client suits me the data. Tell me how you can get the user input without going through the login and password, and use [Authorize].
Sorry for my english.
I decided my task! Below is the code:
public class MachineKeyProtector : Microsoft.Owin.Security.DataProtection.IDataProtector
{
private readonly string[] _purpose =
{
typeof(OAuthAuthorizationServerMiddleware).Namespace,
"Access_Token",
"v1"
};
public byte[] Protect(byte[] userData)
{
throw new NotImplementedException();
}
public byte[] Unprotect(byte[] protectedData)
{
return System.Web.Security.MachineKey.Unprotect(protectedData, _purpose);
}
}
Use:
var secureDataFormat = new TicketDataFormat(new Providers.MachineKeyProtector());
AuthenticationTicket ticket = secureDataFormat.Unprotect(access_token);
var userId = ticket.Identity.GetUserId();

Categories