SignalR - Надсилання повідомлення конкретному користувачеві за допомогою (IUserIdProvider) * NEW 2.0.0 *


101

В останній версії Asp.Net SignalR був доданий новий спосіб передачі повідомлення конкретному користувачеві за допомогою інтерфейсу "IUserIdProvider".

public interface IUserIdProvider
{
   string GetUserId(IRequest request);
}

public class MyHub : Hub
{
   public void Send(string userId, string message)
   {
      Clients.User(userId).send(message);
   }
}

Моє запитання: як мені знати, кому я надсилаю своє повідомлення? Пояснення цього нового методу дуже поверхневе. А проект Заяви SignalR 2.0.0 з цією помилкою так і не складається. Хтось реалізував цю функцію?

Детальніше: http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections#IUserIdProvider

Обійми.


1
Вам потрібно вивчити Аутентифікацію та Авторизацію за допомогою SignalR. UserId буде частиною провайдера IPrincipal.
Gjohn

Відповіді:


153

SignalR забезпечує ConnectionId для кожного з'єднання. Щоб знайти, яке з'єднання належить кому (користувачеві), нам потрібно створити відображення між з'єднанням і користувачем. Це залежить від того, як ви ідентифікуєте користувача у вашій програмі.

У SignalR 2.0 це робиться за допомогою вбудованого IPrincipal.Identity.Name, який входить до ідентифікатора користувача, встановленого під час автентифікації ASP.NET.

Однак вам може знадобитися зіставити з'єднання з користувачем, використовуючи інший ідентифікатор замість Identity.Name. Для цього цей новий провайдер може використовуватися з вашою спеціальною реалізацією для зіставлення користувача з підключенням.

Приклад відображення користувачів SignalR до з'єднань за допомогою IUserIdProvider

Давайте припустимо, що наша програма використовує userIdдля ідентифікації кожного користувача. Тепер нам потрібно надіслати повідомлення конкретному користувачеві. У нас є userIdі message, але SignalR також повинні знати відповідність між нашою USERID і зв'язку.

Для цього нам спочатку потрібно створити новий клас, який реалізує IUserIdProvider:

public class CustomUserIdProvider : IUserIdProvider
{
     public string GetUserId(IRequest request)
    {
        // your logic to fetch a user identifier goes here.

        // for example:

        var userId = MyCustomUserClass.FindUserId(request.User.Identity.Name);
        return userId.ToString();
    }
}

Другий крок - сказати SignalR використовувати наш CustomUserIdProviderзамість реалізації за замовчуванням. Це можна зробити в Startup.cs під час ініціалізації конфігурації концентратора:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var idProvider = new CustomUserIdProvider();

        GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);          

        // Any connection or hub wire up and configuration should go here
        app.MapSignalR();
    }
}

Тепер ви можете надіслати повідомлення певному користувачеві, використовуючи його, userIdяк зазначено в документації, наприклад:

public class MyHub : Hub
{
   public void Send(string userId, string message)
   {
      Clients.User(userId).send(message);
   }
}

Сподіваюся, це допомагає.


Привіт друже, вибачте за пізні відгуки! Але я намагався отримати доступ до ідентифікатора, який генерує CustomUserIdProvider, але метод "OnConnected" - це не те саме. Як я можу зв’язати користувача з базою даних? Дякую!
Ігор

7
Що таке MyCustomUserClass?
Danny Bullis

2
"MyCustomUserClass" може бути вашим користувацьким класом користувача, який містить метод FindUserId. Це лише для прикладу. Ви можете мати будь-який метод у будь-якому класі, який повертає вам UserId і використовує його тут.
Сумний

5
Дякуємо @Sumant, моя проблема закінчилася тим, що b / c я був у проекті Web API, де я реалізував OAuth 2 з маркером носія, що мені довелося реалізувати логіку, щоб передавати маркер носія на рядок запиту, оскільки його не можна витягнути з заголовки цього початкового запиту на підключення сигналу. Не вдалося просто використати request.User.Identity.Name
xinunix

1
@Sumant Я вже вирішив це. Проблема полягала в тому, що я поставив app.MapSignalR();файл global.asax перед автентифікацією.
Містер Робот

38

Ось початок. Відкрийте для пропозицій / удосконалень.

Сервер

public class ChatHub : Hub
{
    public void SendChatMessage(string who, string message)
    {
        string name = Context.User.Identity.Name;
        Clients.Group(name).addChatMessage(name, message);
        Clients.Group("2@2.com").addChatMessage(name, message);
    }

    public override Task OnConnected()
    {
        string name = Context.User.Identity.Name;
        Groups.Add(Context.ConnectionId, name);

        return base.OnConnected();
    }
}

JavaScript

(Зверніть увагу на те, як addChatMessageі sendChatMessageякі також є методи у коді сервера вище)

    $(function () {
    // Declare a proxy to reference the hub.
    var chat = $.connection.chatHub;
    // Create a function that the hub can call to broadcast messages.
    chat.client.addChatMessage = function (who, message) {
        // Html encode display name and message.
        var encodedName = $('<div />').text(who).html();
        var encodedMsg = $('<div />').text(message).html();
        // Add the message to the page.
        $('#chat').append('<li><strong>' + encodedName
            + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
    };

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

Тестування введіть тут опис зображення


Привіт друже, вибачте за пізні відгуки! Але як я можу запобігти втраті CONNECTIONID? Дякую.
Ігор

5
@lgao Я поняття не маю.
Людина-булочка

чому потрібен цей рядок --- Clients.Group ("2@2.com"). addChatMessage (ім'я, повідомлення); ??
Томас

@Thomas я, мабуть, включив його заради демонстрації. Має бути інший спосіб трансляції певної групи, оскільки це було жорстко закодовано.
Чоловік-булочка

Це просте рішення вирішило мою проблему, щоб надіслати повідомлення конкретному користувачеві, який зареєструвався. Його просто, швидко та легко зрозуміти. Я би підтримав цю відповідь кілька разів, якби міг.
Рафаель AMS

5

Ось як слід використовувати SignarR для націлювання на конкретного користувача (без використання будь-якого постачальника):

 private static ConcurrentDictionary<string, string> clients = new ConcurrentDictionary<string, string>();

 public string Login(string username)
 {
     clients.TryAdd(Context.ConnectionId, username);            
     return username;
 }

// The variable 'contextIdClient' is equal to Context.ConnectionId of the user, 
// once logged in. You have to store that 'id' inside a dictionaty for example.  
Clients.Client(contextIdClient).send("Hello!");

2
як ти користуєшся цим, не contextIdClientзрозумів :(
Нео,

2

Подивіться на тести SignalR для функції.

Тест "SendToUser" автоматично приймає ідентифікацію користувача, передану за допомогою звичайної бібліотеки аутентифікації власного власника.

Сценарій полягає в тому, що у вас є користувач, який підключився з декількох пристроїв / браузерів, і ви хочете переслати повідомлення на всі його активні з'єднання.


Спасибі людино! Але версія версії 2.0 тут не збирає SignalR. : (. На жаль, я не можу отримати доступ до нього.
Ігор

0

Стара нитка, але щойно натрапила на це у зразку:

services.AddSignalR()
            .AddAzureSignalR(options =>
        {
            options.ClaimsProvider = context => new[]
            {
                new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
            };
        });
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.