專案實作

Article

用 SignalR 建立即時聊天室

整理 React 與 .NET 串接即時聊天室時,群組訊息到提及與關鍵字解析的實作方式。

ASP.NET Core SignalR 概述

公司專案需求需要有一個類似 Line 聊天室功能,包含關鍵字查詢、公告、回覆訊息、@ 人員、群組等核心功能,其他需求就相較普遍的 CRUD 功能,這篇就不另外敘述。

  • 開發工具:React、React Router 7
  • 訊息處理工具:@microsoft/signalr、linkifyjs、react-mentions-ts
  • 資料處理:@tanstack/react-query、react-hook-form、zustand、zod

當提到聊天室功能,第一個想到的就是使用 WebSocket 與後端介接;但如果從零實作,會遇到斷線、重連等不少細節。因此我選擇使用 .NET SignalR,並透過用戶端的 withAutomaticReconnect 設定重連策略。

SignalR 優先使用 WebSocket 進行連線,若瀏覽器不支援,則會退回使用 SSE 或 Long Polling。這讓我能將時間放在聊天室的事件設計與使用者體驗,而不是自行處理所有連線底層機制。

訊息處理

前端使用 SignalR 監聽後端事件時,方法名稱必須一致。因此前後端需要先制定一套事件名稱規範,讓發送、接收與錯誤處理有一致的對應關係。

SignalR 事件名稱對應示意圖

signalREvents.js

下方先在前端寫好對應發送、監聽事件的方法,可以看到 invoke 其實也是去呼叫後端對應方法。

useSignalR.js

可以看到下方後端也定義了對應的方法名稱,待前端確定連線後就可以開始 invoke 此方法進行訊息傳輸。

SignalRCore.cs

接著可以從瀏覽器 Network 面板查看連線內容所發送的訊息。

SignalR 連線 Network 面板示意圖

@ 人員功能

本來有看到一位作者的 signavio/react-mentions 專案,就是在做這類功能,但缺乏長期維護以及對 TypeScript 型別衍生支援問題,因此該套件已停止更新。不過多虧開源社群,另一位開發者 Fork 該專案後,針對型別以及現代化工具進行重構,最終使用 react-mentions-ts

使用起來很簡單,只需定義好觸發條件以及 @ 文字的規則就能開始使用。

MentionsTextArea.jsx

@ 人員功能示意圖

解析訊息關鍵字

開發時發現某些訊息的特定欄位需要特別處理,譬如網址、Gmail 等關鍵字。但 react-mentions-ts 提供的 parseMentionsMarkup 還不足以達到需求,因為這個 parser 只涵蓋到 @ 人員的關鍵字。

因此我使用 LinkifyJS,專門將網址、Gmail 等文字轉成 <a> 標籤。

關鍵字與網址解析示意圖

群組聊天

看似麻煩,實際流程是後端在建立連線時,先把使用者加入到指定群組後,再用相同方式發送訊息給前端。

useSignalR.js
SignalRCore.cs

注意事項

  • 前端的 connection.on 監聽必須要在呼叫 start() 連線前就先定義好。

    useSignalR.js
  • 前端 invoke 方法送入的欄位,也要與後端方法的參數順序對應。

    useSignalR.js
    SignalRCore.cs

參考資料