Вызов метода Hub из действия контроллера в приложении ASPNetCore MVC с использованием SignalR

Я бы хотел, чтобы веб-приложение ASPNetCore2.0, над которым я работаю, отправляло уведомление определенным пользователям, использующим SignalR. Я хотел бы вызвать метод концентратора из действия другого контроллера (в отличие от клиентского JS-вызова).

Я узнал, что SignalR предназначен для использования не так, но я нашел много пользователей, у которых было такое же «желание», а также некоторые решения. Я проверил несколько предложенных решений, но самый простой и понятный, похоже, был принятым ответом на этот пост: Получить контекст концентратора в SignalR Core из другого объекта.

Итак, я попробовал, и у меня вообще не было ошибок. Выходные данные сервера не содержат ошибок, как и консоль браузера и вкладки сети (я использую Chrome). При отладке процесс плавный, и программа делает именно то, что должна делать ... за исключением того, что пользователи не получают никаких уведомлений ...

Кто-нибудь из вас заметил проблему?

Я создал класс, содержащий общие методы для концентратора:

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);
        }
    }
}

Затем я создал хаб:

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);
        }
    }
}

Эти биты добавлены в метод Startup ConfigureServices:

[...]// Services before these...
services.AddSignalR();
services.AddScoped<HubMethods>();

services.AddMvc();

И метод настройки Startup:

app.UseSignalR(routes =>
{
    routes.MapHub<PostsHub>("/postshub");
});

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

Затем эти строки к представлению:

<script src="~/lib/signalr/signalr.js"></script>
@await Html.PartialAsync("_NotifyScriptsPartial")

А это "_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\'>&times;</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>

Наконец, в контроллере, который должен отправлять уведомление, я добавил следующее:

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();
        }
    }
}

Любая идея?


person Joe F    schedule 12.06.2018    source источник
comment
Вы когда-то пробовали напрямую закачать хаб (удалить методы хаба для тестов)?   -  person Stephu    schedule 12.06.2018
comment
@Stephu Нет, я не сделал этого, потому что был уверен, что это не сработает, но я сделаю это, как только вернусь к работе завтра.   -  person Joe F    schedule 13.06.2018
comment
@Stephu Я пробовал сейчас, и нет, тоже не работает.   -  person Joe F    schedule 13.06.2018
comment
Вы ввели контекст концентратора, как описано здесь: stackoverflow.com/questions/46904678/   -  person Stephu    schedule 14.06.2018
comment
@Stephu Типа ... Я попробовал решение 1 из этой ветки, но, как вы можете видеть, пример довольно урезанный, и я действительно не знал, что делаю, поэтому я испортил его. Затем я попытался напрямую внедрить концентратор (а не контекст концентратора) в свой контроллер. Я провел несколько часов между этим и другим в поиске в Google, пока я не смог больше себе это позволить, и отказался от этого. Я изменил все остальное и устроил так, чтобы подход к вызову хаба из js ...   -  person Joe F    schedule 14.06.2018
comment
Неправильная ступица впрыска. Последнее предложение вашего комментария мне непонятно. Пожалуйста, обновите этот вопрос до вашего фактического кода   -  person Stephu    schedule 14.06.2018
comment
@Stephu В последнем предложении я имел в виду, что я полностью изменил свой код и больше не пытаюсь вызвать метод концентратора из действия другого контроллера. Вместо этого я использую SignalR так, как он разработан для использования, то есть для вызова метода концентратора из клиентского js файла. И конечно работает. Просто я искал способ отправить сообщение клиентам в результате бизнес-логики, а не действия пользователя. Но все равно спасибо!   -  person Joe F    schedule 14.06.2018


Ответы (1)


В Asp.Net Core 2.1 я могу использовать концентратор, как этот, он решает мою проблему, вы также использовали это в своем контроллере. Надеюсь, это поможет.

public class SomeController : Controller
{
    private readonly IHubContext<MyHub> _myHub;

    public SomeController (IHubContext<MyHub> myHub)
    {
        _myHub = myHub;
    }

    public void SomeAction()
    {
        //for your example 
        _myHub.Clients.All.SendAsync("Notify", "data");
    }
}

Я могу получить текст «данных» из консоли браузера. Если вы используете jQuery в своем проекте, добавьте эти коды между jQuery(document).ready(function () { });, потому что вы пытались загрузить частичный html, и я думаю, что ваш код должен запускаться после события ready (). Извините, если я вас неправильно понял.

person hrnsky    schedule 14.08.2018
comment
Спасибо за ваш ответ. К сожалению, публикации пару месяцев назад, поэтому мне пришлось обойти проблему, и сейчас я работаю над чем-то другим, но я попробую ваше решение при следующей возможности. Большое спасибо. - person Joe F; 17.08.2018
comment
Нет проблем @JoeF, я пытался узнать, как вызвать концентратор в действии контроллера, и это работает в моей ситуации. Надеюсь, это поможет другому парню, который попытается сделать что-то подобное. - person hrnsky; 17.08.2018