I have inside my hub a method that is used to send to specific clients that a ticket was updated. However I want it to send it to the server inside the controller and to specific users, you can see how I am doing it below:
I am using the following method to send to the specific clients:
public void NotifyUpdatedTicket(Ticket ticket, string note, params string[] aliases)
{
foreach (string a in aliases)
{
foreach (string connectionId in _allConnections.GetConnections(a))
{
Clients.Client(connectionId).notifyUpdatedTicket(ticket, note);
}
}
}
The _allConnections is the same as the class in http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections#inmemory
However instead of passing 1 user I passed a list of aliases and then get their connections and send to their connection ids.
However it does not seem to be sending properly it sends it to all clients. Here is the command I am using to call the method in my controller:
GlobalHost.ConnectionManager.GetHubContext<TicketHub>().Clients.All.notifyUpdatedTicket(viewModel.Current, viewModel.NewNote, viewModel.Current.OpenedBy, viewModel.Current.AssignedTo);
When I send the above command with the specific OpenedBy and AssignedTo, it sends it all users when it shouldn't.
Am I missing something from calling it in the controller that sends it only to specific users or something?
Even when I put in the persons alias directly into the hub method it still sends to everyone. I assume this has to do with the controller method.
This
Clients.All.notifyUpdatedTicket
will send a message to all clients. It won't call your method with this signature (I assume that's what you're trying to do):
public void NotifyUpdatedTicket(Ticket ticket, string note, params string[] aliases)
I assume this is a method you added to your hub class.
It's not a good idea to put methods in a hub class if their sole purpose is to be called from the server-side.
Anyway, if you do, you need to use GetHubContext<TicketHub>() from within that method even if it is part of the hub class, because the hub context is set up as part of the hub pipeline when a request to that hub arrives. Without that, the hub is just a regular class and you don't have access to dynamic members like Clients.
So, all in all, it seems like you're confused about some fundamentals. I'd suggest moving NotifyUpdatedTicket to a separate class that also caches the relevant hub context, then call that method when needed (e.g. from a MVC controller method if that is your intent).
Related
We use SignalR in several applications – it’s very handy and works well! :-) We usually have a C# server side and a JavaScript client.
But now we have a “special need”: On the server side, we would like to have one and the same method executed regardless of what message type the client sends. A kind of “catch-all method” in the SignalR server. (It’s for a special testing purpose – there will be added new message types all the time. The message parameter is always an object.)
Is this possible? I know about the HubMethodNameAttribute, and I basically would like to be able to use some sort of wildcard there. Something equal to this in the hub class:
[HubMethodName("*")]
public Task MyCatchAll(object par)
{
// handle the message
}
Or some other way to always get the same method called, regardless of message type.
We don’t want to have to maintain a list of all possible messages, like this:
public Task OneOfTheMessageTypes(object par) => MyCatchAll(par);
public Task AnotherMessageType(object par) => MyCatchAll(par);
public Task AndYetAnotherOne(object par) => MyCatchAll(par);
public Task AndSoOn(object par) => MyCatchAll(par);
...
(I’ve tried using an IHubFilter, but that isn’t called until SignalR has identified the method to call; can’t be used for message types not defined.)
I’d be most grateful for some help from a SignalR expert! :-)
/Anders from Sweden
So this is maybe pretty simple for network programming dudes but I'm a little confused about this. Let me try to ask given an example of usage.
Say I were to create a chat server with clients being able to join with a username. No accounts or passwords. Just username.
I have a working Echo Server example running that uses async calls so all that is good. Now I need to define some message handling between the client and the server.
Lets say the client now connects and it wants to get the list of connected users. How would I go about doing that?
My thought is that I create a function called BeginGetConnectedUsers and that will send message to the server. The server will then reply with a list of users but because I'm using async calls I'll now have my accept code look at the message and figure out that it is a reply from a BeginGetConnectedUsers so it will send the data to a EndGetConnectedUsers.
But I have no idea if this is a good way to do something like this?
But also with this design I'll have to pair every BeginGet function with an EndGet function. This could be made more readable with the async await style but though but that may not be preferable either.
I have no experience with how to structure the following communication between client and server when they have to start exchanging all the interesting data stuff...
And advice? Places to look? All my google searches that include the work TCPLISTENER will all show me examples of Echo Servers and I already know how to do that.
There are many posible implementations here.
I woudl implement an strategy pattern or a controller pattern. Other options are state machines (deterministic automatas) or even a simple and big switch case.
So basically you have only one function to receive the messages from the wire.
All the messages implements the same interface may be
IMessage<T>
{
string Type {get;set;}
T Data {get;set;}
}
So when you get the message you use the Type property to route the T Data to the actual method you want.
In a simple implementation using a controller, you anotate the controller methods with an attribute indicating the message type you want to manage:
class Controller
{
[Messagetype("GetConnectedUsersResponse")]
Response GetConnectedUsers(IEnumerable<User> users)
{
//...
}
[Messagetype("AnothermessageType")]
Response OtherStuffToDo(....)
{
//...
}
}
When you receives the message, by using some simple reflection you dynamically call to the method wich has the attibute with the matching message type attribute.
If you dont like reflection another option (among a lot of docens else) is to use an strategy patter
So you can register some message Handlers by key in your IoC container for example.
All hadlers implement a function lets say
interface MessageHandler<T>
{
Response Handle(T Data);
}
When you receive the message you just resolve the handler using your favourite IoC container (resolving by name is lately considered as an atipattern, so take it with a pinch of salt)
var handler = container.resolve(message.Type);
var response = handler.handle(message.Data);
In both implementations you should define how you respond (if you do) and adjust the "Response" return type (May be you just dont have response so it is void)
I am using Signalr 1.1.4 because im still using .net4 so cant upgrade to signalr 2.
Basically i want to post a message from the server to just the caller to avoid messages being sent to any client that did not start the process off.
My hub class looks like this
public class UpdateHub : Hub
{
/// <summary>
/// Sends the message.
/// </summary>
/// <param name="progressMessage">The progress message.</param>
public void SendMessage(string progressMessage)
{
Clients.Client(Context.ConnectionId).sendMessage(string.Format(progressMessage));
}
}
my javascript looks like this
// get handle to subscriptionUpload hub generated by SignalR
var updateHub = $.connection.UpdateHub;
// establish the connection to the server and start server-side operation
$.connection.hub.start();
updateHub.client.sendMessage = function (message)
{
$("container").empty();
$("container").append(message);
}
Now in my controller action method i would like to do something like this
UpdateHub hub = new UpdateHub();
hub.SendMessage("process has started");
//continue on with long process
hub.SendMessage("process has ended");
Is this even possible?
What we can find in documentation documentation:
You don't instantiate the Hub class or call its methods from your own
code on the server; all that is done for you by the SignalR Hubs
pipeline. SignalR creates a new instance of your Hub class each time
it needs to handle a Hub operation such as when a client connects,
disconnects, or makes a method call to the server.
Because instances of the Hub class are transient, you can't use them
to maintain state from one method call to the next. Each time the
server receives a method call from a client, a new instance of your
Hub class processes the message. To maintain state through multiple
connections and method calls, use some other method such as a
database, or a static variable on the Hub class, or a different class
that does not derive from Hub. If you persist data in memory, using a
method such as a static variable on the Hub class, the data will be
lost when the app domain recycles.
And then:
If you want to send messages to clients from your own code that runs
outside the Hub class, you can't do it by instantiating a Hub class
instance, but you can do it by getting a reference to the SignalR
context object for your Hub class.
You can get the context of your hub: GlobalHost.ConnectionManager.GetHubContext<YourHub>()
and then you can use it to call methods on the client side like this:
context.Clients.All.YourMethod(params);
or
context.Clients.Client(someConnectionID).YourMethod(params);
But in this case you won't be able to use Context.ConnectionId in this methods, because you don't have a direct connection to your hub. In this case you will need to store your connections somewhere (static variables, cache, db etc) and then use it to determine which client should be called.
Hope it will help.
I have a (c#) server and client with webhook implementation.
I have a custom authentication handler to authenticate client receivers when they subscribe for events.
This works ok.
Now, I want to trigger an event from inside the server's code on some condition. At that moment, I call method:
this.NotifyAsync()
located on source code:
namespace System.Web.Http
public static class ApiControllerExtensions
public static Task<int> NotifyAsync(this ApiController controller, string action, object data)
Problem is that this call to NotifyAsync() will check that there is an authenticated user, which is not the case because is the server itself triggering this action..
Did I need to generate an AuthenticationTicket() from inside servers code in order to trigger webhook notification or there is other way to do it ??
thanks
just calling NotifyAllAsync() !!! no authentication required here and it will send notification to all clients...
According to the documentation I have read, in order to send a message to a client, I just need to call:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.foo(msg);
So... does the Hub class need any methods? If not, then all I have is an empty class:
public class MyHub : Hub
{
}
which just seems like a pointless setup. Am I implementing this incorrectly? because it makes more sense to have methods in a hub class, and then call those methods to send a message to the client.
Also, in the hub itself, I can access Context.connectionId, so that I can get the requestor's connection Id and then stop the message from being fired to that client.... If a Hub shouldn't have methods, then is there a way to access the requestor's connection id?
Yes, you need an empty HUB class declaration, because - It is actually just a proxy between the JS client and the controller so it could be empty since all methods are called via the Clients dynamic variable.
I mean without this , you can work, but you have to write JS for that. This is explained briefly in following link.
For more info refer this link - http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-javascript-client#genproxy