I'm trying to get list of connected users by using the following server code in SignalR hub. For store in-memory data I'm using following class:
public class UserInfo
{
public string ConnectionId { get; set; }
public string UserName { get; set; }
public string Role { get; set; }
}
When user connected I'm adding user to the list of connected users:
public override Task OnConnected()
{
if (Context.User.Identity.IsAuthenticated)
{
if (Context.User.IsInRole("User"))
{
ui.Add(new UserInfo { ConnectionId = Context.ConnectionId, UserName = Context.User.Identity.Name, Role = "User" });
}
else
{
ui.Add(new UserInfo { ConnectionId = Context.ConnectionId, UserName = Context.User.Identity.Name, Role = "Operator" });
}
}
return base.OnConnected();
}
Here is the way I'm getting list of currently connected users:
public IEnumerable<UserInfo> GetUsers()
{
var x = (from a in ui where a.Role == "User" select new UserInfo { UserName = a.UserName, ConnectionId = a.ConnectionId }).ToList();
return x;
}
public IEnumerable<UserInfo> GetOperators()
{
var y = (from a in ui where a.Role == "Operator" select new UserInfo { UserName = a.UserName, ConnectionId = a.ConnectionId }).ToList();
return y;
}
Unfortinately public method GetOperators/GetUsers not accessible and I did not receive data on client side:
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
//Here I'm calling hub public methods
chat.getOperators = function (data) {
alert(data);
};
chat.getUsers = function (data) {
alert(data);
};
// Create a function that the hub can call to broadcast messages.
chat.client.addChatMessage = function (name, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.sendChatMessage($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
Your syntax for the calls to the server is wrong; this here:
chat.getUsers = function (data) {
alert(data);
};
will simply define chat.getUsers to be a function.
You probably want
chat.server.getUsers().done(function(data) {
console.log(data);
}).fail(function(error) {
console.log("failed to get data", error);
});
Take another look at the documentation.
please can you try this
//on your client action do a server call
chat.server.getOperators();
and
//on your client action do a server call
chat.server.getUsers();
instead of
chat.getOperators = function (data) {
alert(data);
};
chat.getUsers = function (data) {
alert(data);
};
Related
I'm working on a PoC for a notification engine. I'm able to successfully connect and call Hub functions from JS, but I can't seem to get push notifications to work. I'm getting an Object reference not set to an instance of an object error.
Triggering class
// I was able to confirm that the connectionIds are valid
public void HandleEvent(NewNotificationEvent eventMessage)
{
// _connections handles connectionids of a user
// multiple connection ids to handle multiple open tabs
var connectionIds = _connections.GetConnectionsByUser(eventMessage.Notification.UserId);
foreach(var connectionId in connectionIds)
{
// a client is returned, but aside from the connectionid, all the properties are either null or empty
var client = _notificationHub.Clients.Client(connectionId);
///// ERROR HAPPENS HERE
///// I'm guessing NewNotification() isn't defined somewhere, but I don't know where.
client.NewNotification("Hello");
}
}
View.cshtml
var notificationHub = $.connection.NotificationHub;
$.connection.hub.qs="userId=#userId"
// Initialization
$.connection.hub.start().done(function () {
// get count unread notification count
notificationHub.invoke("unReadNotificationsCount")
.done((unreadCount) => {
if (unreadCount > 0) {
$('#notificationBadge').html(unreadCount);
hasNewNotification = true;
}
console.log('SignalR connected!');
})
.fail((data) => {
console.log('Unable to reach server');
console.log(data);
})
.always(() => $('#dropdownNotificationOptions').show());
});
// also tried notificationHub.NewNotification()
notificationHub.client.NewNotification = function (notification) {
console.log(notification);
}
NotificationHub.cs
[HubName("NotificationHub")]
public class NotificationHub : Hub
{
//ctor
public override Task OnConnected()
{
var userId = Context.QueryString["userid"];
if(userId.IsNotNullOrEmpty())
_connections.Add(Context.ConnectionId, Guid.Parse(userId));
else
_connections.Add(Context.ConnectionId, Guid.NewGuid());
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled = true)
{
_connections.Remove(Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
Guid userId;
if (Guid.TryParse(Context.QueryString["userid"],out userId))
{
//var userId = _workContext.CurrentUser.Id;
var userConnection = _connections.GetUserByConnection(Context.ConnectionId);
if (userConnection == null || userConnection.IsNotNullOrEmpty())
{
_connections.Add(Context.ConnectionId, userId);
}
}
return base.OnReconnected();
}
}
You should have your NewNotification before the $.connection.hub.start() such as:
var notificationHub = $.connection.NotificationHub;
$.connection.hub.qs="userId=#userId"
// Moved to define before the connection start
notificationHub.client.NewNotification = function (notification) {
console.log(notification);
}
// Initialization
$.connection.hub.start().done(function () {
// get count unread notification count
notificationHub.invoke("unReadNotificationsCount")
.done((unreadCount) => {
if (unreadCount > 0) {
$('#notificationBadge').html(unreadCount);
hasNewNotification = true;
}
console.log('SignalR connected!');
})
.fail((data) => {
console.log('Unable to reach server');
console.log(data);
})
.always(() => $('#dropdownNotificationOptions').show());
});
I am trying to send some objects back to client after connecting, but somehow they are receiving them as empty arrays {} when List<T> is sent and as [object Object] for
here is my hub code:
public class MyHub : Hub
{
private static readonly List<User> Users = new List<User>();
private const string DateTimeFormat = "HH:mm:ss tt zzz";
public async Task Join(string username, UserType usertype, string locale, CallerInfo callerInfo)
{
// Add the new user
var user = Users.FirstOrDefault(e => e.Username == username);
if (user != null)
{
Users.Remove(user);
}
Users.Add(new User
{
Username = username,
ConnectionId = Context.ConnectionId,
Type = usertype,
Locale = locale,
ConnectionStartTime = DateTime.Now.ToString(DateTimeFormat),
CallerInfo = callerInfo
});
await SendUserListUpdate(StatusConstants.Join, usertype);
}
public async Task SendUserListUpdate(string status, UserType userType, string conID = "")
{
object[] args = { Users, status, conID == string.Empty ? Context.ConnectionId : conID, userType };
await Clients.All.SendCoreAsync(MethodNameConstants.UpdateUserList, args);
}
}
and here is my client code:
"use strict";
var locale = 'ar-SA';
var username = 'test-user';
var usersList;
var connection = new signalR.HubConnectionBuilder().withUrl("/MyHub").withAutomaticReconnect().build();
// register listeners
connection.on('UpdateUserList',
function(users,status, connectionId, userType) {
usersList = users;
console.log(users);
console.log(usersList);
console.log('status : '+ status);
console.log('connection id : '+ connectionId);
});
// join client list
connection.start().then(async function () {
console.log('trying to join users list');
await connection.invoke('Join',username,'guest', locale, null);
console.log('join successful');
}).catch(function (err) { console.error(err); });
I am trying to receive correct objects on client side so that I can handle them correctly. I couldn't figure out why it is received like this
I created a test source which should send a message to the client every x time. This is the ApiController:
public class TestSourceController : ApiController
{
private static readonly ConcurrentQueue<StreamWriter> ConnectedClients = new ConcurrentQueue<StreamWriter>();
[AllowAnonymous]
[Route("api/sources/test")]
public HttpResponseMessage Get()
{
var response = Request.CreateResponse();
response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>) OnStreamAvailable,
"text/event-stream");
return response;
}
private static void OnStreamAvailable(Stream stream, HttpContent headers, TransportContext context)
{
var clientStream = new StreamWriter(stream);
ConnectedClients.Enqueue(clientStream);
}
private static void DoThings()
{
const string outboundMessage = "Test";
foreach (var clientStream in ConnectedClients)
{
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage));
clientStream.Flush();
}
}
}
The clientStream.Flush(); is called like expected and without exceptions.
I handle it in AngularJS like this:
$scope.handleServerCallback = function (data) {
console.log(data);
$scope.$apply(function() {
$scope.serverData = data;
});
};
$scope.listen = function () {
$scope.eventSource = new window.EventSource("http://localhost:18270/api/sources/test");
$scope.eventSource.onmessage = $scope.handleServerCallback;
$scope.eventSource.onopen = function() { console.log("Opened source"); };
$scope.eventSource.onerror = function (e) { console.error(e); };
};
$scope.listen();
My guess is it's a problem with the server since I can see the "EventStream" from the test call is empty in the chrome debugger.
Does anyone know how to make sure the messages arrive at the client?
The solution was quite easy, according to the spec every line has to end with "\n" and the very last line with "\n\n".
So:
clientStream.WriteLine("data:" + JsonConvert.SerializeObject(outboundMessage) + "\n\n");
Solves it.
I am creating a test app, where one can download some files and on download success notification will be propagated to admin ,something like notification in
www.ge.tt,or panel notification in Facebook.
I have two pages
a)Download.aspx
b)LandingPage.aspx
In Download.aspx
function PushNotification() {
alert("I ran Upto Here");
//Declare a proxy to Reference a Hub
var notification = $.connection.notificationHub;
//Start a Connection
$.connection.hub.start().done(function () {
notification.server.send(21);
//$("#hdnFileId").val()
alert("I ran Upto Here 2 ");
});
notification.client.broadcastMessage = function (FileID) {
alert("file was Downloaded" + FileID);
};
}
Here two different tabs/browser are working Fine showing alert message if page Loads.
but i want to use the brodcast message in my LandingPage.aspx
here is the Js
$(function () {
var notification = $.connection.notificationHub;
notification.client.broadcastMessage = function (FileID) {
alert("file was Downloaded" + FileID);
};
});
And my hubclass ..
namespace TestApplication.Entities
{
public class NotificationHub : Hub
{
//public void Hello()
//{
// Clients.All.hello();
//}
public void Send(int FileID)
{
Clients.All.broadcastMessage(FileID);
}
}
}
but the notification is not coming here, whats wrong Here?
You didn't start connection in LandingPage.aspx
Try like this in LandingPage.aspx
var notification = $.connection.notificationHub;
$.connection.hub.start();
notification.client.broadcastMessage = function (FileID) {
alert("file was Downloaded" + FileID);
};
I am trying to return the count of all connections to a web client with SignalR. I increment and persist the client count by firing logic on the hub OnConnected() method.
public class PopHub : Hub
{
public static List<string> Users = new List<string>();
public override Task OnConnected()
{
var clientId = GetClientId();
if (Users.IndexOf(clientId) == -1)
{
Users.Add(clientId);
}
Send(Users.Count);
return base.OnConnected();
}
public void Send(int count)
{
Clients.All.updateUsersOnlineCount(count);
}
stepping through my code with an external console client (to trigger OnConnected()) shows that I am traversing through Send(int count) with a count of 1.
On my web client, I configure my JS as such
$(function() {
var hub = $.connection.popHub;
hub.client.updateUsersOnlineCount = function(count) {
console.log(count);
};
$.connection.hub.start().done(function() {
console.log('connected');
});
}());
And lastly my snippet from the generated js
proxies.popHub = this.createHubProxy('popHub');
proxies.popHub.client = { };
proxies.popHub.server = {
popClient: function (message) {
return proxies.popHub.invoke.apply(proxies.popHub, $.merge(["PopClient"], $.makeArray(arguments)));
},
query: function () {
return proxies.popHub.invoke.apply(proxies.popHub, $.merge(["Query"], $.makeArray(arguments)));
},
send: function (count) {
return proxies.popHub.invoke.apply(proxies.popHub, $.merge(["Send"], $.makeArray(arguments)));
}
};
**Note that Popclient and Query are unrelated server side events, of which do work giving me somewhat of a sanity check. Any idea why my clients updateUsersOnlineCount function is not logging the count of connections as I expect?
Instead of doing it in the OnConnected, please give this a try, it might be that the Base.OnConnected has not been executed yet, so it's not ready to broadcast to clients.
//Client
$.connection.hub.start().done(function() {
console.log('connected');
hub.server.ClientCount();
});
//Hub
public static List<string> Users = new List<string>();
public override Task OnConnected()
{
var clientId = GetClientId();
if (Users.IndexOf(clientId) == -1)
{
Users.Add(clientId);
}
//Send(Users.Count); //not calling this since it's not working
return base.OnConnected();
}
public void ClientCount()
{
Clients.All.updateUsersOnlineCount(Users.Count);
}