[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 方便,實際上可以用一個頁面來處理,這些差異的部分由後端產生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 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)

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