.NET Core API application which I want to use push real time messages to SPA. I have working example with azure function but now I want to convert it to Web API.
The working Azure function looks like this:
[FunctionName("Push")]
public static Task PushInfoSuccess([HttpTrigger(AuthorizationLevel.Anonymous, "post")] ILogger log, Models models
[SignalR(HubName = "Hub1")] IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
UserId = models.UserId,
Target = "Hub1",
Arguments = new[] { models}
});
}
I want to rewrite using .NET Core API.
I created hub class like below
public class ChatHub : Hub
{
public Task BroadcastMessage(string name, string message) =>
Clients.All.SendAsync("broadcastMessage", name, message);
public void Send(UserModel userModel)
{
Clients.User(userModel.UserId).SendAsync(userModel.Message);
}
public Task Echo(string name, string message) =>
Clients.Client(Context.ConnectionId)
.SendAsync("echo", name, $"{message} (echo from server)");
}
I have this model class:
public class UserModel
{
public string UserId { get; set; }
public string Message { get; set; }
}
Now I have some other application which will call my application through API so I will add controller
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public ActionResult Get(UserModel userModel)
{
return Ok();
}
}
My other application will invoke this API to push notification to SPA. When pushing notification I want to push it to specific userid which I will get UserID and Message through API. Now I want to push messages to UserModel.UserID. When sending message to specific user, Do I need to consider connection Id as well? If I have multiple hubs then do I get different connectionid for each hub? In my SPA application I have more than one hub. So what would be the relationship between the connection id and userid? Can someone help me here to understand and help me? Thanks
Connectionids plus userid will become uniquness right?
Yes right you are, This should be like below:
HubController:
public class HubController : Controller
{
private readonly IHubContext<NotificationUserHub> _notificationUserHubContext;
private readonly IUserConnectionManager _userConnectionManager;
public HubController(IHubContext<NotificationHub> notificationHubContext, IHubContext<NotificationUserHub> notificationUserHubContext, IUserConnectionManager userConnectionManager)
{
_notificationUserHubContext = notificationUserHubContext;
_userConnectionManager = userConnectionManager;
}
[HttpPost]
public async Task<ActionResult> SendToSpecificUser(HubModel model)
{
var connections = _userConnectionManager.GetUserConnections(model.userId);
if (connections != null && connections.Count > 0)
{
foreach (var connectionId in connections)
{
await _notificationUserHubContext.Clients.Client(connectionId).SendAsync("sendToUser", model.Title, model.Message);
}
}
return View();
}
}
}
Notification User Hub:
public class NotificationUserHub : Hub
{
private readonly IUserConnectionManager _userConnectionManager;
public NotificationUserHub(IUserConnectionManager userConnectionManager)
{
_userConnectionManager = userConnectionManager;
}
public string GetConnectionId()
{
var httpContext = this.Context.GetHttpContext();
var userId = httpContext.Request.Query["userId"];
_userConnectionManager.KeepUserConnection(userId, Context.ConnectionId);
return Context.ConnectionId;
}
//Called when a connection with the hub is terminated.
public async override Task OnDisconnectedAsync(Exception exception)
{
//get the connectionId
var connectionId = Context.ConnectionId;
_userConnectionManager.RemoveUserConnection(connectionId);
var value = await Task.FromResult(0);//adding dump code to follow the template of Hub > OnDisconnectedAsync
}
}
User Connection Manager:
public class UserConnectionManager : IUserConnectionManager
{
private static Dictionary<string, List<string>> userConnectionMap = new Dictionary<string, List<string>>();
private static string userConnectionMapLocker = string.Empty;
public void KeepUserConnection(string userId, string connectionId)
{
lock (userConnectionMapLocker)
{
if (!userConnectionMap.ContainsKey(userId))
{
userConnectionMap[userId] = new List<string>();
}
userConnectionMap[userId].Add(connectionId);
}
}
public void RemoveUserConnection(string connectionId)
{
//Remove the connectionId of user
lock (userConnectionMapLocker)
{
foreach (var userId in userConnectionMap.Keys)
{
if (userConnectionMap.ContainsKey(userId))
{
if (userConnectionMap[userId].Contains(connectionId))
{
userConnectionMap[userId].Remove(connectionId);
break;
}
}
}
}
}
public List<string> GetUserConnections(string userId)
{
var conn = new List<string>();
lock (userConnectionMapLocker)
{
conn = userConnectionMap[userId];
}
return conn;
}
}
Model:
public class HubModel
{
public string Title { get; set; }
public string Message { get; set; }
public string userId { get; set; }
}
Hope it will help you.