C# MVC - Send message with SignalR from server to Caller - c#

I have a request from client to Server using SignalR-2.2.1. After that request, Server will send the results back to the caller.
Here is my JavaScript code :
$(document).ready(function () {
var myHub = $.connection.signalHub;
myHub.client.receive = function (tmp) {
var obj = $.parseJSON(tmp);
//do something
};
$.connection.hub.start().done(function () {
$('#xxxxx').click(function () {
var form = $("form");
form.submit();
myHub.server.sendSomething();
});
}).fail(function (reason) {
//do something
});
});
And here is the code from my Server Side
public partial class SignalHub : Hub
{
public void SendSomething()
{
Clients.All.receive(MyJson);
}
}
When I use Clients.All function, it works perfectly fine. But what I want is the server only send the result back to the caller (the one that send the request).
So I change Clients.All.receive(MyJson) to Clients.Caller.receive(MyJson). After I change it, now the client doesnt update the content. Do I need to specify my receive function in client side that it would receive something different?

My guess is that since you are calling form.submit(); you are probably navigating away from the page or possibly reloading it, in that sense a new connection with a different "connectionId" is established between the new/reloaded page, thus the message intended for Client.Caller is lost since it is associated with the connection established on the page that you navigated away from or refreshed.
You might want to consider posting your form-data using AJAX instead of a full form submit.

Related

Calling SignalR from API at another project - No error nor notification

I have a WebSite integrated with SignalR. It functions well, and it has a button which sends popup notification to all clients who are online. It works well when I click on the button.
My API is in another project but in the same Solution. I want to send the above notification by calling from the API side. Basically, a mobile app will send a request to API and then API will send a notification to all online web clients.
Below code runs and not gives the notification nor any error.
Is this fundamentally correct? Appreciate your help
API code (at WebAPI project)
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
GMapChatHub sendmsg = new GMapChatHub();
sendmsg.sendHelpMessage(record_id.ToString());
return "Done";
}
C# code (at Web project)
namespace GMapChat
{
public class GMapChatHub : Hub
{
public void sendHelpMessage(string token)
{
var context = GlobalHost.ConnectionManager.GetHubContext<GMapChatHub>();
context.Clients.All.helpMessageReceived(token, "Test help message");
}
}
}
Home.aspx file (at Web project)
var chat = $.connection.gMapChatHub;
$(document).ready(function () {
chat.client.helpMessageReceived = function (token,msg) {
console.log("helpMessageReceived: " + msg);
$('#helpMessageBody').html(msg)
$('#helpModal').modal('toggle');
};
}
You can not call that hub directly. Firs you need to install the .net client for SignalR from nuget. Then you need to initialize it like this :
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
using (var hubConnection = new HubConnection("your local host address"))
{
IHubProxy proxy= hubConnection.CreateHubProxy("GMapChatHub");
await hubConnection.Start();
proxy.Invoke("sendHelpMessage",record_id.ToString()); // invoke server method
}
// return sth. IEnumerable<string>
}
And opening a new connection per request may not be good idea you may make it per session (if you use) or static or time fashioned.

Can't send message to specific user with SignalR

I can't make works the message sending to one specific user from the code behind. Clients.All works, Clients.AllExcept(userId) works, but not Client.User(userId).
My hub:
public class MessagingHub : Hub
{
public override Task OnConnected()
{
var signalRConnectionId = Context.ConnectionId;
// for testing purpose, I collect the userId from the VS Debug window
System.Diagnostics.Debug.WriteLine("OnConnected --> " + signalRConnectionId);
return base.OnConnected();
}
}
My controller to send message from code behind:
public void PostMessageToUser(string ConnectionId)
{
var mappingHub = GlobalHost.ConnectionManager.GetHubContext<MessagingHub>();
// doesn't works
mappingHub.Clients.User(ConnectionId).onMessageRecorded();
// doesn't works
mappingHub.Clients.Users(new List<string>() { ConnectionId }).onMessageRecorded();
// works
mappingHub.Clients.All.onMessageRecorded();
// works (?!)
mappingHub.Clients.AllExcept(ConnectionId).onMessageRecorded();
}
How my hub is initialized on the JS:
var con, hub;
function StartRealtimeMessaging()
{
con = $.hubConnection();
hub = con.createHubProxy('MessagingHub');
hub.on('onMessageRecorded', function () {
$(".MessageContainer").append("<div>I've received a message!!</div>");
});
con.start();
}
And finally how I send a(n empty) message to the hub:
function TestSendToUser(connectionId)
{
$.ajax({
url: '/Default/PostMessageToUser',
type: "POST",
data: { ConnectionId: connectionId},// contains the user I want to send the message to
});
}
So, it works perfectly with mappingHub.Clients.All.onMessageRecorded(); but not with mappingHub.Clients.User(ConnectionId).onMessageRecorded(); or mappingHub.Clients.Users(new List<string>() { ConnectionId}).onMessageRecorded();.
But interestingly, it works with mappingHub.Clients.AllExcept(ConnectionId).onMessageRecorded(); : All users connected receive the message except the given userid, which means the userid is good, and the user is well identified. So, why Clients.User(ConnectionId) doesn't works?
If you want to send a message to one particular connection and when you want to use the ConnectionId, make sure you use Clients.Client, and not Clients.User
Like this:
public void PostMessageToUser(string connectionId)
{
var mappingHub = GlobalHost.ConnectionManager.GetHubContext<MessagingHub>();
// Like this
mappingHub.Clients.Client(connectionId).onMessageRecorded();
// or this
mappingHub.Clients.Clients(new List<string>() { connectionId }).onMessageRecorded();
}
I had the same problem. I couldn't get .User(ConnectionId) to work.
I have just spent days trying to get SignalR to report progress on a long processing job to only the client who requested the job. That is, it isn't a chat app which most of the examples describe.
Any 'long processing progress reporting' examples I found only have a sim of the job in the hub. I have a controller doing real work and need to send messages from the controller, not the hub.
I used this answer https://stackoverflow.com/a/21222303/3251300. as a workaround for your stated problem but have included all the code snippets I use for the long processing job in case they are useful for anyone who stumbles on this answer.
The workaround has an elegance in that it uses the .Group() feature. By setting each groupID equal to the internal userID, messages can be sent using .Group(userID) without having to separately maintain a list of the userID/connectionID relationships outside SignalR.
There may be a way to maintain the relationships in SignalR without using the .Group() feature but I haven’t found it yet.
Pass the userID to the view using a hidden type which then makes it available to the js.
<input type="hidden" value="#ViewBag.UserID" id="userID" />
Then in the js hub script use the following to send the userID to the hub when the hub connection starts up.
$.connection.hub.start()
.done(function () {
var userID = document.getElementById('userID').value;
$.connection.myHub.server.announce(userID);
})
.fail(function () { alert("Hub failed to start.") });
The hub then has one statement which associates the userID and connectionID to the groupID, which is then the same string as the userID.
public class MyHub : Hub
{
public void Announce(string userID)
{
Groups.Add(Context.ConnectionId, userID);
}
}
To send messages from the controller (Again, not the hub in this case, the message is reporting progress to the client on a long processing request running in the controller) after setting the hub context, use .Group() and the internal userID.
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
string fileMessage = "Some message";
hubContext.Clients.Group(userID).hubMessage(fileMessage);
This is then displayed in the view using the js to place the message in a div
$.connection.myHub.client.hubMessage = function (message) {
$("#hubMessages").html(message);
}
'#hubMessages' refers to this div in the view. Examples use .append which makes the div grow each time you send a message, .HTML replaces whatever is in the div with the new message.
<div id="hubMessages"></div>
Anyone who comes to this answer and is trying to get going on MVC and SignalR, a big shout out to Caleb who has a great series of intro vids for SignalR https://youtu.be/kr8uHeNjOKw Anyone who finds this answer who is new to SignalR I recommend you spend an hour watching these.
I face same problem.
I change from:
Clients.User(connectionId).SendAsync(CallbackDefinition.DirectMessage, directMessageResult);
to:
Clients.Client(connectionId).SendAsync(CallbackDefinition.DirectMessage, directMessageResult);
And it work :D
Thank to: Matthieu Charbonnier

SignalR Server side method call from Client side in Non MVC project

I'm familiar with WebMethods and PageMethods to call Server side methods from the client side without refreshing page.
I'm achieving to Invoke Server-side method (present inside index.aspx.cs).
Here is what i'm trying:
Server Side Created Hub class:
public class MyHub : Hub
{
public void RefreshData(string imessage)
{
Clients.All.displayData(imessage);
}
}
Client side:
<script>
var isconnected = false;
(function () {
$.connection.myHub.client.displayData = function (thisdata) {
$('ul').append('<li>' + thisdata + '</li>');
};
$("#btnadd").click(function () {
if (isconnected) {
$.connection.myHub.server.refreshData($("#txtval").val());
}
});
$.connection.hub.start()
.done(function () {
isconnected = true;
})
.fail(function () {
isconnected = false;
});
})();
</script>
Above things are working fine, Client is calling Server-side's RefreshData Method and Server is passing message to the Client-side's displayData method.
My Question is: As same as AJAX WebMethod () .. Is it possible to call any method of index.aspx.cs (not inside the MyHub class) ?
If i talk about calling Client-side method from the index.aspx.cs, then we can try:
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.displayData(imessage);
But if i search about listening client side's method call from any page.aspx.cs, i'm not getting anything..
I hope i have explained the issue, if not..please excuse me..
No, you cannot call aspx.cs method (outside Hub) using SignalR connection. What is extact scenario for which you need to invoke aspx.cs method using SignalR? As mentioned by you, use ajax for invoking aspx.cs method.
Infact for code snippet mentioned above, ajax is best suited (not
SignalR).
SignalR is mainly used when you need open connection between client and server (ex- using websocket, one of transport option support with SignalR). It is more appropriate if you need to push message from server to client without client requesting it.
// Invoke this from server when server want to push some information to client without client requesting this information
$.connection.myHub.client.updateData = function (thisdata) {
$('ul').append('<li>' + thisdata + '</li>');
};

SignalR message not working when sending from server side to client

This is my HTML:
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.khaosHub;
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (message) {
// Html encode display name and message.
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li>' + encodedMsg + '</li>');
};
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
console.log("sending");
// Call the Send method on the hub.
chat.server.send("something");
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
</script>
My Hub:
public class KhaosHub : Hub
{
public void Send(string message)
{
Clients.All.broadcastMessage(message);
}
}
When I click #sendmessage my Send method in KhaosHub is triggered which I have verified using a breakpoint and my message does get sent to the div via broadcastMessage.
Note: I've not included my call to app.MapSignalR in the example above as I know it's working from the client side.
The issue I have is when I call broadcastMessage from some back end code it doesn't work. I am calling it via:
var context = GlobalHost.ConnectionManager.GetHubContext<KhaosHub>();
context.Clients.All.broadcastMessage("some message");
When I debug the Clients.All property, I can't see any clients (I don't know if I should be able to but thought I'd add that information.
Any ideas?
EDIT: This is my startup class for the hub:
[assembly: OwinStartup(typeof (Startup))]
namespace CC.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
Thanks for the question. Following up on the comments I have tracked my own problem down also to not getting the correct hubcontext from the GlobalHost.ConnectionManager.
To solve this I specifically set a DependencyResolver on the GlobalHost and passing this Resolver to the HubConfiguration used to MapSignalR.
In code that is:
Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver =
New Microsoft.AspNet.SignalR.DefaultDependencyResolver
app.MapSignalR(
New Microsoft.AspNet.SignalR.HubConfiguration With
{.Resolver = Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver})
You may want to convert this VB.Net code to C#.

Redirecting a user from within a SignalR Hub class

I have a button that users can click to bid on something. Each bid, broadcasts the latest bid to every other client. That's the reason why I'm using SignalR.
Now, the user needs to have active credits, and if he doesn't have credits I want to redirect him somewhere.
The more obvious approach fails me, so any suggestion is welcome.
//Does the user have credits to spend?
if (user.LanceCreditBalance >= 1)
{
//populate the "ar" object and send it out to everybody.
var result = Json.Encode(ar);
Clients.addMessage(result);
}
else
{
//And this isn't working as expected. Doesn't redirect
//And causes a 302 error when viewing the Firebug console in Firefox.
HttpContext.Current.Response.Redirect(#"http://www.google.com");
}
The above code is all within the Chat class which inherits from the SignalR.Hub class.
Server:
if(user.LanceCreditBalance >= 1)
{
var result = Json.Encode(ar);
// send Message to all clients
Clients.addMessage(result);
}
else
{
// Invoke a js-Function only on the current client
Caller.redirectMe("http://www.google.com");
}
Client:
$(function () {
var chat = $.connection.chat;
chat.addMessage = function(message) {
// do something
};
// function the server can invoke
chat.redirectMe = function(target) {
window.location = target;
};
$.connection.hub.start();
});

Categories

Resources