I would like the ASPNetCore2.0 webapp I'm working on to send a notification to specific users using SignalR. I would like to call the hub's method from another controller's action (as opposed to a client's JS call).
I have learned that this is not how SignalR is intended to be used, but I've found many users who had the same 'desire' and also some solutions. I have checked several proposed solutions, but the simplest and cleaner seemed to be the accepted answer to this post: Get Hub Context in SignalR Core from within another object.
So I gave it a go, and I get no errors at all. The server's output is error-free, and so are the browser's console and network tabs (I'm using Chrome). When debugging, the flow is smooth and the program does exactly what it should do... except the users don't get any notification...
Do any of you spot the problem?
I created a class that contains the shared methods for the hub:
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Hubs
{
public class HubMethods
{
private readonly IHubContext<PostsHub> _hubContext;
public HubMethods(IHubContext<PostsHub> hubContext)
{
_hubContext = hubContext;
}
public async Task Notify(string postId, string sender, List<string> users)
{
await _hubContext.Clients.Users(users).SendAsync("Notify", sender, postId);
}
}
}
Then I created a hub:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Hubs
{
[Authorize]
public class PostsHub : Hub
{
private HubMethods _hubMethods;
public PostsHub(HubMethods hubMethods)
{
_hubMethods = hubMethods;
}
public async Task Notify(string postId, string sender, List<string> users)
{
await _hubMethods.Notify(postId, sender, users);
}
}
}
Added these bits to Startup's ConfigureServices method:
[...]// Services before these...
services.AddSignalR();
services.AddScoped<HubMethods>();
services.AddMvc();
And Startup's Configure method:
app.UseSignalR(routes =>
{
routes.MapHub<PostsHub>("/postshub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Then these lines to the view:
<script src="~/lib/signalr/signalr.js"></script>
@await Html.PartialAsync("_NotifyScriptsPartial")
And this is "_NotifyScriptsPartial.cshtml":
<script>
var connection = new signalR.HubConnectionBuilder().withUrl('/PostsHub').build();
connection.on('Notify', function (sender, postId) {
var post = postId;
var sentBy = sender;
var content = '<a href=\'#\' class=\'close\' data-dismiss=\'alert\' aria-label=\'close\'>×</a>' +
'You just received a new comment from <strong>' +
sentBy + '</strong>. Click <a href = \'#\' class=\'alert-link\' >here</a> to view the post.'
var alert = document.createElement('div');
alert.classList.add('alert', 'alert-info', 'alert-dismissible');
alert.html(content);
document.getElementById('pageContent').appendChild(alert);
});
</script>
Finally, in the controller that is supposed to send the notification, I added these:
public class PostsController : Controller
{
private readonly HubMethods _hubMethods;
public PostsController(HubMethods hubMethods)
{
_hubMethods = hubMethods;
}
// POST: Create a new post
[Authorize]
[HttpPost]
public async Task<IActionResult> Create(DetailsModel model, List<string> readers)
{
if (ModelState.IsValid)
{
// Do stuff here... including creating the newPostId, userId and receipients variables used below
await _hubMethods.Notify(newPostId, userId, receipients);
// Do more stuff and eventually...
return View();
}
}
}
Any idea?