Reputation: 685
.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
Upvotes: 0
Views: 1423
Reputation: 22543
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.
Upvotes: 2
Reputation: 602
i have my setup like this
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chat");
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = configuration["Tokens:Issuer"],
ValidAudience = configuration["Tokens:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Tokens:Key"]))
};
cfg.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
}
And the class that inherits the Hub like below
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ChatHub : Hub
{
public async Task SendMessage(MessageModel msg)
{
if (!string.IsNullOrEmpty(msg.ClientUniqueId))
{
await Clients.Client(msg.ClientUniqueId).SendAsync("ReceiveMessage", chat);
}
}
}
Upvotes: 0