1. 程式人生 > >ASP.NET Core 2.2 基礎知識(十六) SignalR (未完待續)

ASP.NET Core 2.2 基礎知識(十六) SignalR (未完待續)

我一直覺得學習的最好方法就是先讓程式能夠正常執行,才去學習他的原理,剖析他的細節.

就好像這個圖:

 

所以,我們先跟著官方文件,建立一個 SignalR 應用: https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-2.2&tabs=visual-studio

這個例子一共涉及到下面幾個步驟:

  • 自定義中心 ChatHub ;
  • 在啟動類 Startup 中啟用 SignalR 服務,並新增路由;
  • 編寫客戶端JS
  • 下載 SignalR 官方JS.

自定義中心 : ChatHub

    public class ChatHub : Hub
    {    
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }

 

"Hub" 一詞,有的地方翻譯成"集線器",有的地方翻譯成"中心",對於我這種非科班出身的人來說,還是"中心"聽起簡單點.

對於基類 Hub ,xml 是這樣說的 : A base class for a SignalR hub.   SignalR 中心的基類.

這個很簡單,看這些名字就知道他們是幹嘛用的,具體的描述可以看官方文件.

中心的作用可以這樣簡單的描述:

通過中心,我們能在伺服器的程式碼中定義客戶端可以呼叫的方法(必須是 public);

通過中心,我們能在客戶端的程式碼中定義伺服器可以呼叫的方法.

上述程式碼,我們通過繼承 Hub ,定義了一個自己的中心 : ChatHub (聊天中心) ,在這個類裡面,我們做了下面兩件事:

  • 定義了客戶端可以呼叫的方法 : SendMessage(string user, string message)
  • 呼叫了所有連線上 Hub 的客戶端的 ReceiveMessage 方法,並將 user,message 兩個字串作為入參傳入該方法.

 

中心定義好了,肯定需要啟用

在啟動類 Startup 中啟用 SignalR 服務,並新增路由

        public void ConfigureServices(IServiceCollection services)
        {
            ......
    
            //註冊 SignalR 服務
            services.AddSignalR();
        }

 

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ......

            //設定 SignalR 中心路由
            app.UseSignalR(routes => { routes.MapHub<ChatHub>("/chatHub"); });

            app.UseMvc();
        }

 

編寫客戶端JS 

"use strict";//不太明白這句話是什麼意思...

//建立一個連線到我們建立的 ChatHub 的 connection.
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

//定義客戶端的方法,方法名: ReceiveMessage ,兩個入參.
//注意,這個方法就是伺服器要呼叫的方法,伺服器和客戶端的名字一定要一樣.
connection.on("ReceiveMessage", function (user, message) {
    var msg = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    var encodedMsg = user + " says " + msg;
    var li = document.createElement("li");
    li.textContent = encodedMsg;
    document.getElementById("messagesList").appendChild(li);
});

//開啟連線.
connection.start().catch(function (err) {
    return console.error(err.toString());
});

document.getElementById("sendButton").addEventListener("click", function (event) {
    var user = document.getElementById("userInput").value;
    var message = document.getElementById("messageInput").value;

    //呼叫伺服器的 SendMessage 方法,並傳入兩個入參.這和委託的呼叫太像了.
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});

 

上面的 ChatHub 類的 SendAsync 方法在呼叫客戶端的方法時,方法名是直接寫的 字串 : "ReceiveMessage"

官方不推薦這樣寫,因為不是強型別,可能出現執行時錯誤,因此建議使用強型別的中心

(順帶附上了一些額外的功能):

    //建議使用下面的強型別方式
    //方法二
    public interface IChatClient
    {
        //就算是這種強型別方式,客戶端定義的方法名也必須和這個方法名一樣,包括簽名.
        Task ReceiveMessage(string user, string message);
    }

    public class StronglyTypedChatHub : Hub<IChatClient>
    {
        //[HubMethodName("hello")] 可以改名,如果改了名,前端也要跟著改,別忘了.
        public async Task SendMessage(string user, string message)
        {
            //呼叫客戶端定義的 ReceiveMessage 方法.
            //throw new HubException("哈哈,出錯了!");//可以向客戶端傳送異常.只會向當前呼叫的客戶端傳送,並且只發送 message ,不會發送堆疊資訊.
            await Clients.All.ReceiveMessage($"{GetHashCode()}" + user, message);//傳遞 hashCode 是為了證明,每次呼叫都是不同的例項.所以官方說不要在"中心"裡面存狀態.
        }

        //該方法可以在客戶端連線上後,執行操作
        public override async Task OnConnectedAsync()
        {
            await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
            await base.OnConnectedAsync();
        }

        //同理,當客戶端斷開連線時執行的操作
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
            await base.OnDisconnectedAsync(exception);
        }
    }

 

當然,註冊路由的程式碼也得換了 :  app.UseSignalR(routes => { routes.MapHub<StronglyTypedChatHub>("/chatHub"); });

 

好睏,明天繼續