SignalR - Passing back a Client's response to the server - signalr

I am calling a client-side method from the server that is essentially a Javascript confirm dialog. If the client clicks theCancel button on the dialog I want to delete their token from local storage (essentially logging them out) ...
sessionStorage.removeItem('access-token');
If the client clicks the OK button I want to do the same on a different client (I have the connectionId of this client stored on the server side). So I need a way for the server to know which option the client chose (OK or Cancel).
How would I go about taking the client's choice and passing it back to the server, so the server could call the "Logout" Javascript on the proper client?

I think you have a couple of options.
You could send a message back to the Hub once the user has clicked on a button.
Hub
[HubName("message")]
public class MessageHub : Hub
{
public void sendmessage(bool logout)
{
Clients.Caller(????).logoutuser(logout); //However you identify who to logout??
}
}
Client
var hub = $.connection.message;
hub.client.logoutuser = function(message) {
if (message.logout = true) {
sessionStorage.removeItem('access-token');
}
}
hub.server.sendmessage(true); //result of the user's click OK -> True, Cancel -> False
$.connection.hub.start().done(function () { });
Or you could hit an API that gets the connection for some? user who you're wanting to log out.
API
[RoutePrefix("api/messaging")]
public class MessagingController : ApiController
{
[Route("")]
public void Post(Message message)
{
var notificationHub = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
if (notificationHub != null)
{
try
{
notificationHub.Clients.User(message.UserName).logoutuser(message);
}
catch (Exception ex)
{
}
}
}
}
Client
function notifyController(responseObj) {
$.ajax({
url: '/api/Messaging/',
type: 'POST',
data: responseObj, // some object containing the users response information?
success: function (data) { return; },
error: function (ex) { return; }
});
}

Related

SignalR Private Chat Not Returning Data

I implemented SignalR to my project. But I have problem sending private message. I am sending 'toConnectionID' from page.
ChatHub.cs
public void LoadPrivateMessages(string toUserName,string toConnectionID)
{
var chatHub = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
string fromUserName = Context.QueryString["userName"].ToString();
string fromConnectionID = Context.ConnectionId;
List<ChatMessageDetail> currentMessages = cachePrivateMessages.Where(x => x.ToUserName == toUserName && x.FromUserName == fromUserName && x.ToConnectionID==toConnectionID && x.FromConnectionID==fromConnectionID).ToList();
chatHub.Clients.Client(toConnectionID).privateMessagesHistory(currentMessages);
}
My currentMessages list is filling. I am OK here. But I can't take messages to page.
Javascript
chatHub.client.privateMessagesHistory = function (data) {
console.log(data);
};
My console screen is null after this JavaScript code.
Edit :
That is my connection code in document.ready function. Second is sending information about receiver.
var chatHub = $.connection.chatHub;
$.connection.hub.qs = { "userName": "#Session["userName"]" };
$.connection.hub.start().done(function () {
console.log("Connection OK !");
});
$(document).on('click','.onlineUser', function () {
var userName = $(this).attr('id');//That is which user to send(Receiver)
var toConnectionID = $(this).attr('connectionID');
chatHub.server.loadPrivateMessages(userName, toConnectionID);
$('.privateMessagediv').show();
$('.userName').html("");
$('.userName').append("<h4>" + userName + "</h4>");
$('.btnSendingPrivateMessage').attr('id', userName);
$('.btnSendingPrivateMessage').attr('connectionid', toConnectionID);
chatHub.client.privateMessagesHistory = function (data) {
$('#privateChatBox').append(data);
};
});
Edit 2 :
I solved issue.
chatHub.Clients.Client(fromConnectionID).privateMessagesHistory(currentMessages);
instead of
chatHub.Clients.Client(toConnectionID).privateMessagesHistory(currentMessages);
My original suspicion was with the toConnectionId, but after examining your privateMessageHistory callback registration, it's more likely a timing issue.
You need to setup the client callback before the hub .start().
From Microsoft:
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method
There's no need to place your callback registration in the click event.
var chatHub = $.connection.chatHub;
$.connection.hub.qs = { "userName": "#Session["userName"]" };
// callback first
chatHub.client.privateMessagesHistory = function (data) {
$('#privateChatBox').append(data);
};
// start the hub connection - note chatHub is different from your .hub
chatHub.start().done(function () {
console.log("Connection OK !");
});

Asp.Net SignalR second tab does not fire onConnected event

I just created a sample project with signalR. I am just trying to test managing multiple connection. Everything works as expected when I open the first browser and load the page. It is going to fire the OnConnected event on the hub. But when I open another browser or different tab and load the page, it doesn't fire OnConnected event anymore. It shows $.connection.hub.id though.
Here is the hub
[HubName("genie")]
public class Genie : Microsoft.AspNet.SignalR.Hub
{
private static ConnectionManager _manager = new ConnectionManager();
[HubMethodName("AdminCommand")]
public void AdminCommand(string command, string message = "")
{
var connetions = _manager.GetConnections();
connetions.Remove(Context.ConnectionId);
Clients.Clients(connetions).onAdminCommand(command, message);
}
public override Task OnConnected()
{
_manager.AddConnection(Context.ConnectionId);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
_manager.RemoveConnection(Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
}
And here is the javascript code:
var proxy = $.connection.genie;
$.connection.hub.start()
.done(function (state) {
console.log($.connection.hub.id);
});
proxy.on('onAdminCommand', function (command, message) {
if (command == "HappyGenie") {
$scope.goTo("happy/");
} else if (command == "SadGenie") {
$scope.goTo("sad/");
} else if (command == "CustomAnnouncement") {
dataService.setDataByKey("Announcement", message);
$scope.goTo("customannouncement/");
}
});
I establish a connection with the generated proxy.
Is there something I am doing wrong?
Thanks

How to use SignalR to re-direct after Session timeout?

I am using SignalR to redirect my app after session timeout:
void Session_End(object sender, EventArgs e)
{
var HubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
HubContext.Clients.All.clientListener(false);}
And my Hub class:
public class MyHub : Hub
{
public void ServerListener(bool result)
{
Clients.Caller.ClientListener(result);
}
}
JavaScript:
(function () {
var myHub = $.connection.myHub;
$.connection.hub.start()
.done(function () {
console.log("Connected");
})
.fail(function () {
alert("Failed!");
})
myHub.client.clientListener = function (data) {
if (data == false)
window.location.href = "/Home/Index";//#Url.Action("Index","Home");
}
})();
The Problems that I face is:
When multiple users are connected all users are logged out at the same time, even if they logged in at different times.
Logout redirection occurs even if regular requests are made.
It would really help me, if someone could tell me how to do a "server push" without invoking the Session_End() as I want to use some other session state other than "In Proc".
Regarding Question #1:
Since you're using HubContext.Clients.All.clientListener all users connected to SignalR are indeed going to receive the message from the server - it doesn't matter when they logged in to your app.
You should use this guide to send a message from the server to a specific user: https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/mapping-users-to-connections .
Personally I like single-user groups idea https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/mapping-users-to-connections#single-user-groups.
As for the additional question, as long as you can get the hub reference with GlobalHost.ConnectionManager.GetHubContext<MyHub>() all you have to do is call a registered client side method to perform a server push (e.g. Clients.Group(userid).clientListener(false)). It's not something that has to be done exclusively on Session_End().
What I did is:
On Page_Load I update a hidden label with the Session.SessionId
Pass it via query string before starting hub on client:
$.connection.hub.qs = { "sessionId": $("#lblSessionId").text() };
On the server side in the hub OnConnected/OnReconnected I call this method to store the correlation of sessionId->connectionId in dictionary:
private void AddConnectedClient()
{
// read the [ASP.Net] sessionId we send from client in queryString
string sessionId = Context.Request.QueryString["sessionId"];
if (!string.IsNullOrEmpty(sessionId))
sessionIdToConnectionId.AddOrUpdate(sessionId, Context.ConnectionId, (k, v) => Context.ConnectionId);
}
Now in the Session_End I have this code (GetConnectionIdFromSessionId is a static method I added in hub to read from sessionIdToConnectionId dictionary shown in #3):
protected void Session_End(object sender, EventArgs e)
{
// look for signalR connecitonId
string connectionID = YourHub.GetConnectionIdFromSessionId(Session.SessionID);
if (connectionID != null)
GlobalHost.ConnectionManager.GetHubContext<YourHub>().Clients.Client(connectionID).onSessionExpired();
}

Public method not firing in SignalR

I have a simple application, like a chat, integrated with SignalR. I added a new method on my Hub and a new function on client side, like you can see below.
The problem is, my method called SendMessageChat isn't firing, because occurs the following error
TypeError: chat2.server.SendMessageChat is not a function
but the method chat2.server.send works fine, and I don't know why my second method doesn't work. Can someone help me ?
JavaScript
$(function () {
var chat2 = $.connection.redirectTask;
chat2.client.broadcastMessage = function (name, message) {
// Do something here
};
chat2.client.sendMessage = function (name, message) {
// Do something here
};
//$.connection.hub.logging = true;
$.connection.hub.start().done(function () {
/* BUTTON CLICK IN ANOTHER PAGE */
$('#btnFinish').click(function () {
chat2.server.send($.cookie("User"), $("#lista :selected").text());
});
/* CASE HIT ENTER INSIDE THE TEXT FIELD IN CHAT */
$(document).on("keypress", "#txtChat", function (e) {
var code = (e.keyCode ? e.keyCode : e.which);
if (code == 13) {
var message = $(this).val();
$(this).val("");
chat2.server.SendMessageChat($.cookie("User"), message);
}
});
});
});
SERVER SIDE
public class RedirectTask : Hub
{
public void Send(string nome, string message)
{
Clients.All.broadcastMessage(name, message);
}
public void SendMessageChat(string nome, string message)
{
Clients.All.sendMessage(name, message);
}
}
Reference
Need to change to
chat2.server.sendMessageChat($.cookie("User"), message);
Camel-casing of method names in JavaScript clients
By default, JavaScript clients refer to Hub methods by using a camel-cased version of the method name. SignalR automatically makes this change so that JavaScript code can conform to JavaScript conventions.
Server
public void NewContosoChatMessage(string userName, string message)
JavaScript client using generated proxy
contosoChatHubProxy.server.newContosoChatMessage(userName, message);
If you want to specify a different name for clients to use, add the HubMethodName attribute.
Server
[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)
JavaScript client using generated proxy
contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

OnConnected throws an exception - how can I remove connection?

I have a class that inherits PersistentConnection. When I override OnConnected I check a few of the querystring parameters passed in to make sure the user is authenticated. If not I throw an exception but the client is still considered connected. How can I remove the client from the connected clients list?
public class NotificationConnection : PersistentConnection
{
protected override Task OnConnected(IRequest request, string connectionId)
{
if (String.IsNullOrWhiteSpace(request.QueryString["example"]))
throw new SecurityException("whatever");
return base.OnConnected(request, connectionId);
}
protected override Task OnDisconnected(IRequest request, string connectionId)
{
return base.OnDisconnected(request, connectionId);
}
}
Consider changing your design to use the method exposed by signalr to validate users are authenticated and they have rights on the Persistent Connection
protected override bool AuthorizeRequest(IRequest request)
{
return request.User != null && request.User.Identity.IsAuthenticated;
}
Why don't you just send a message back to the client telling it to disconnect? e.g.
On the server.
if (String.IsNullOrWhiteSpace(request.QueryString["example"]))
{
Connection.Send(connectionId, "Close");
}
Then on the JS client do something like;
connection.received(function(data) {
if ( data === "Close" ){
connection.stop();
// send the user to another page with window.location or warn them that their connection has been stopped.
}
});
On a .net client;
connection.Received += data =>
{
if ( data == "Close" )
{
connection.stop();
}
};

Resources