[SignalR] 02 - Multi Hubs


接續上一次的練習,這次改用多個 Hub 來處理

在前一篇文章中大概簡單的介紹了一下 web 與 winform 同時使用 singnalR 技術完成即時溝通,但是如果我們的需求比較複雜一點,那可能就沒辦法透過先前的方式完成

模擬團隊使用情境

假設目前有數個團隊,相互之間的溝通僅需要在團隊內即可。各團隊有自己的頻道,不能互相影響。另外團隊管理者也需要有一個管理團隊的頻道便於聯繫,跨部門溝通;最終,還需要有一個公告通知,不管在哪一個團隊,哪一個頻道的成員都會接收到即時通知。

這邊採用的方式是多個 Hub 的解決方案,原本用 Groups 做,做到後面要弄 WinForm 的時候抓瞎了,而且感覺很麻煩,改用了多個 Hub 的解決方案,整體來說後端程式碼變得很簡潔,前端的部分也少了很多 Group 的處理

所有成員依據自己身分,決定是否要加入 Leader、Team 等等頻道,模擬的情境如下

後端 Hub

假設有 Team1、Team2 兩個團隊,再加上 Leader 及公告,所以要有四個 Hub。並且大家都只有一個方法,那就是 Send 訊息給各個在 Hub 註冊的 Client 端

前端

理論上應該只有一個頁面,每個人登入該頁面都可以從後端吃到這個人的權限,能不能發布訊息、參加哪個頻道等等

前端需要做的事情就是連線到 Hub 發訊息,並且接收 Hub 傳來的訊息呈現,但因為我們有多個 Hub,而且又有不一樣的權限,大概就是把一樣的部分共用,不同的部分放在 data,細節就不再處理了,重點只是擺在我們要完成的功能

Html

網頁的部分我用了四個頁面來代表四個人的情境,但實際上所有程式碼都差不多,只有頻道的下拉選單,載入的 data 不一樣,這邊為了 Demo 方便,實際上可以用一個頁面來處理,這些差異的部分由後端產生

<body>
  <label for="name">Name:</label>
  <input id="name" type="text" value="" readonly />
  <select id="channelId">
    <option value="0">Team1</option>
    <option value="2">Leader</option>
    <option value="3">Notice</option>
  </select>

  <input id="msg" value="" />
  <input id="send" type="button" value="Send" />

  <hr />
  <h3 id="channel">Channel</h3>
  <ul id="room"></ul>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <!--需要先載入jQuery-->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/signalr.js/2.3.0/jquery.signalR.min.js"></script>
  <script src="/signalr/hubs"></script>
  <!--指向根目錄的/signalr/hubs-->
  <script src="data1.js"></script>
  <script src="app.js" type="module"></script>
</body>
// data1.js (sample)

let data = {
  name: "張三",
  channel: [
    { name: "team1", id: 0 },
    { name: "leader", id: 2 },
    { name: "notice", id: 3 },
  ],
};

// 其實就是一個類似 factory 的東西,主要在給予前端 hub 的 proxy 物件去操作
export const getProxy = (channelId) => {
  let id = parseInt(channelId, 10);
  switch (id) {
    case 0:
      return $.connection.team1;
    case 1:
      return $.connection.team2;
    case 2:
      return $.connection.leader;
    case 3:
      return $.connection.notice;
    default:
      return $.connection.team1;
  }
};

// app.js
// 這一段比較長,不過大致上重點就是中間那一段,從 data 找到這個人有哪些頻道,
// 然後就去註冊這些頻道的事件給後端 Hub 呼叫
import * as tool from "./common.js";
(function () {
  let $sendBtn = $("#send");
  let $msgDom = $("#msg");
  let $room = $("#room");
  // Data Binding to UI
  $("#name").val(data.name);
  $("#channel").text(data.channel.map((x) => x.name).join("、"));

  for (let index = 0; index < data.channel.length; index++) {
    let currectChannelId = data.channel[index].id;
    let currectProxy = tool.getProxy(currectChannelId);
    currectProxy.client.received = (msg) => $room.append(`<li>${msg}</li>`);
  }

  $.connection.hub.start().done(function () {
    $sendBtn.on("click", function () {
      let currectProxy = tool.getProxy($("#channelId").val());
      let channelName = data.channel.find(
        (x) => x.id === parseInt($("#channelId").val(), 10)
      ).name;

      currectProxy.server.send(
        `[${channelName}]${data.name}${$msgDom.val()}`
      );
      $msgDom.val("");
    });
  });
})();

程式:Github (branch:develop)

這次的解決方案還是有一些問題,等之後再補充