SignalR - Gửi tin nhắn đến một người dùng cụ thể bằng (IUserIdProvider) * MỚI 2.0.0 *


101

Trong phiên bản mới nhất của Asp.Net SignalR, đã được bổ sung một cách mới để gửi tin nhắn đến một người dùng cụ thể, sử dụng giao diện "IUserIdProvider".

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

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

Câu hỏi của tôi là: Làm cách nào để biết tôi đang gửi tin nhắn của mình cho ai? Giải thích về phương pháp mới này rất hời hợt. Và bản thảo Tuyên bố của SignalR 2.0.0 với lỗi này và không biên dịch. Có ai đã triển khai tính năng này?

Thông tin thêm: http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections#IUserIdProvider

Những cái ôm.


1
Bạn cần xem xét Xác thực và Ủy quyền với SignalR. UserId sẽ là một phần của nhà cung cấp IPrincipal.
Gjohn

Câu trả lời:


152

SignalR cung cấp ConnectionId cho mỗi kết nối. Để tìm kết nối thuộc về ai (người dùng), chúng ta cần tạo ánh xạ giữa kết nối và người dùng. Điều này phụ thuộc vào cách bạn xác định người dùng trong ứng dụng của mình.

Trong SignalR 2.0, điều này được thực hiện bằng cách sử dụng sẵn có IPrincipal.Identity.Name, là mã định danh người dùng đã đăng nhập được đặt trong quá trình xác thực ASP.NET.

Tuy nhiên, bạn có thể cần lập bản đồ kết nối với người dùng bằng một số nhận dạng khác thay vì sử dụng Identity.Name. Vì mục đích này, nhà cung cấp mới này có thể được sử dụng với triển khai tùy chỉnh của bạn để ánh xạ người dùng với kết nối.

Ví dụ về ánh xạ người dùng SignalR với kết nối bằng IUserIdProvider

Giả sử ứng dụng của chúng tôi sử dụng a userIdđể xác định từng người dùng. Bây giờ, chúng ta cần gửi tin nhắn đến một người dùng cụ thể. Chúng tôi có userIdmessage, nhưng SignalR cũng phải biết ánh xạ giữa userId của chúng tôi và kết nối.

Để đạt được điều này, trước tiên chúng ta cần tạo một lớp mới triển khai 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();
    }
}

Bước thứ hai là yêu cầu SignalR sử dụng của chúng tôi CustomUserIdProviderthay vì triển khai mặc định. Điều này có thể được thực hiện trong Startup.cs trong khi khởi tạo cấu hình trung tâm:

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

Bây giờ, bạn có thể gửi tin nhắn cho một người dùng cụ thể bằng cách sử dụng tin nhắn của họ userIdnhư đã đề cập trong tài liệu, như:

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

Hi vọng điêu nay co ich.


Chào bạn, xin lỗi vì phản hồi muộn! Nhưng tôi đã cố gắng truy cập Id tạo CustomUserIdProvider nhưng phương pháp "OnConnected" nó không giống nhau. Làm cách nào để liên kết với Người dùng trong cơ sở dữ liệu? Cảm ơn bạn!
Igor

7
MyCustomUserClass là gì?
Danny Bullis

2
"MyCustomUserClass" có thể là lớp người dùng tùy chỉnh của bạn có chứa phương thức FindUserId. Đây chỉ là một ví dụ. Bạn có thể có bất kỳ phương thức nào trong bất kỳ lớp nào trả về UserId cho bạn và sử dụng nó ở đây.
Sumant

5
Cảm ơn @Sumant, vấn đề của tôi đã kết thúc là tôi đang tham gia một dự án API Web nơi tôi đã triển khai OAuth 2 với mã thông báo mang tên tôi, tôi phải triển khai logic để chuyển mã thông báo mang tên trên chuỗi truy vấn vì nó không thể được lấy từ các tiêu đề trên yêu cầu kết nối signalr ban đầu đó. Không thể chỉ sử dụng request.User.Identity.Name
xinunix

1
@Sumant Tôi đã giải quyết điều đó. Vấn đề là tôi đã đặt app.MapSignalR();global.asax trước khi xác thực.
Mr. Robot

38

Đây là một khởi đầu .. Mở cho các đề xuất / cải tiến.

Người phục vụ

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

(Lưu ý cách thức addChatMessagesendChatMessagecũng là các phương thức trong mã máy chủ ở trên)

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

Thử nghiệm nhập mô tả hình ảnh ở đây


Chào bạn, xin lỗi vì phản hồi muộn! Nhưng làm cách nào để ngăn chặn việc mất CONNECTIONID? Cảm ơn bạn.
Igor

5
@lgao Tôi không biết.
The Muffin Man

tại sao dòng này là bắt buộc --- Clients.Group ("2@2.com"). addChatMessage (tên, tin nhắn); ??
Thomas

@Thomas Tôi có lẽ đã bao gồm nó vì lợi ích của bản demo. Phải có một cách khác để phát tới một nhóm cụ thể vì điều này đã được mã hóa cứng.
The Muffin Man

Giải pháp đơn giản này đã giải quyết được vấn đề của tôi khi gửi tin nhắn đến một người dùng đã đăng nhập cụ thể. Nó đơn giản, nhanh chóng và dễ hiểu. Tôi sẽ tán thành câu trả lời này nhiều lần nếu tôi có thể.
Rafael AMS

5

Đây là cách sử dụng SignarR để nhắm mục tiêu một người dùng cụ thể (mà không sử dụng bất kỳ nhà cung cấp nào):

 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
Làm thế nào bạn đang sử dụng điều này không contextIdClienthiểu :(
Neo

2

Nhìn vào Kiểm tra SignalR để biết tính năng này.

Kiểm tra "SendToUser" tự động lấy danh tính người dùng được chuyển bằng cách sử dụng thư viện xác thực cú pháp thông thường.

Tình huống là bạn có một người dùng đã kết nối từ nhiều thiết bị / trình duyệt và bạn muốn gửi thông báo đến tất cả các kết nối đang hoạt động của họ.


Cảm ơn anh bạn! Nhưng phiên bản dự án 2.0 không biên dịch SignalR ở đây. .: (Đáng tiếc là tôi không thể truy cập vào nó.
Igor

0

Chủ đề cũ, nhưng chỉ gặp điều này trong một mẫu:

services.AddSignalR()
            .AddAzureSignalR(options =>
        {
            options.ClaimsProvider = context => new[]
            {
                new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"])
            };
        });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.