Note
這不是這篇文章的最新版本。 關於目前版本,請參閱 本文的 .NET 10 版本。
Warning
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 關於目前版本,請參閱 本文的 .NET 10 版本。
本教學課程示範如何在 ASP.NET Core Web 應用程式中使用 SignalR,以統合及建置以 TypeScript 撰寫的用戶端。 Webpack 可讓開發人員組合並建置 Web 應用程式的用戶端資源。
在本教學課程中,您會了解如何:
- 建立 ASP.NET Core SignalR 應用程式
- 設定 SignalR 伺服器
- 使用 Webpack 設定組建管線
- 設定 SignalR TypeScript 用戶端
- 啟用用戶端與伺服器之間的通訊
檢視或下載範例程式碼 \(英文\) (如何下載)
Prerequisites
最新版本的 Visual Studio 具備 ASP.NET 與網頁開發工作負載。
建立 ASP.NET Core Web 應用程式
根據預設,Visual Studio 會使用在其安裝目錄中找到的 npm 版本。 若要設定 Visual Studio 以在 PATH 環境變數中尋找 npm:
啟動最新版本的 Visual Studio。 在 [開始] 視窗中,選取 [不使用程式碼繼續]。
巡覽至 [工具]>[選項]>[專案和方案]>[網頁套件管理]>[外部 Web 工具]。
從清單中選取
$(PATH)項目。 選取向上箭號,將此項目移至清單中的第二個位置,然後選取 [確定]:
若要建立新的 ASP.NET Core Web 應用程式:
- 請使用 「檔案>新>專案/解決方案... 」選單選項。
- 在 「建立新專案 」對話框中,選擇 ASP.NET Core Empty 範本。 然後選取下一步。
- 在 [設定新專案] 對話方塊中,輸入
SignalRWebpack作為 [專案名稱]。 選取 下一步。 - 在「附加資訊」對話框中,從框架下拉選單選擇 .NET 10.0(長期支援)。 選取 ,創建。
將 Microsoft.TypeScript.MSBuild NuGet 套件新增至專案:
- 在 解決方案總管中,右鍵點擊專案節點,選擇 管理 NuGet 套件...。
- 在 [瀏覽] 索引標籤中搜尋
Microsoft.TypeScript.MSBuild,然後選取右側的 [安裝] 以安裝套件。 - 在 預覽變更 對話框中,選擇 套用。
- 在 [接受授權] 對話方塊中,選取 [我接受]。
Visual Studio 會在方案總管的 [相依性] 節點底下新增 NuGet 套件,而在專案中啟用 TypeScript 編譯。
設定伺服器
在本節中,您會設定 ASP.NET Core Web 應用程式以傳送和接收 SignalR 訊息。
在
Program.cs中,呼叫 AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();同樣地,在
Program.cs中呼叫 UseDefaultFiles 和 UseStaticFiles:var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles();上述程式碼可讓伺服器找出並提供
index.html檔案。 無論使用者輸入檔案的完整 URL 還是 Web 應用程式的根 URL,檔案都會提供。在專案根目錄
Hubs中,為SignalRWebpack/中樞類別建立名為 SignalR 的新目錄。使用下列程式碼建立新檔案
Hubs/ChatHub.cs:using Microsoft.AspNetCore.SignalR; namespace SignalRWebpack.Hubs; public class ChatHub : Hub { public async Task NewMessage(long username, string message) => await Clients.All.SendAsync("messageReceived", username, message); }上述程式碼會在伺服器收到訊息之後,向所有連線的使用者廣播已接收的訊息。 不需要讓泛型
on方法接收所有訊息。 以訊息名稱命名方法就已足夠。在此範例中:
- TypeScript 用戶端會傳送一則識別為
newMessage的訊息。 - C#
NewMessage方法預期有用戶端所傳送的資料。 - 在 SendAsync 上呼叫 。
- 接收的訊息就會傳送到連線至中樞的所有用戶端。
- TypeScript 用戶端會傳送一則識別為
在
using頂端新增下列Program.cs陳述式,以解析ChatHub參考:using SignalRWebpack.Hubs;在
Program.cs中,將/hub路由對應至ChatHub中樞。 將顯示Hello World!的程式碼取代為下列程式碼:app.MapHub<ChatHub>("/hub");
設定用戶端
在本節中,您會建立 Node.js 專案,以使用Webpack將 TypeScript 轉換成 JavaScript,並配套客戶端資源,包括 HTML 和 CSS。
在專案根目錄中執行下列命令,以建立
package.json檔案:npm init -y將醒目提示的屬性新增至
package.json檔案,並儲存檔案變更:{ "name": "signalrwebpack", "version": "1.0.0", "private": true, "description": "", "main": "index.js", "scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" }, "keywords": [], "author": "", "license": "ISC", "type": "commonjs", "devDependencies": { "clean-webpack-plugin": "4.0.0", "css-loader": "7.1.3", "html-webpack-plugin": "5.6.6", "mini-css-extract-plugin": "2.10.0", "ts-loader": "9.5.4", "typescript": "5.9.3", "webpack": "5.104.1", "webpack-cli": "6.0.1" }, "dependencies": { "@microsoft/signalr": "^10.0.0", "@types/node": "^25.0.10" } }將
private屬性設定為true可避免在下一個步驟中出現套件安裝警告。安裝必要的 npm 套件。 從專案根目錄執行下列命令︰
npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli-E選項會停用 npm 將語意版本控制範圍運算子寫入package.json的預設行為。 例如,會使用"webpack": "5.76.1",而不是"webpack": "^5.76.1"。 此選項可防止意外升級至較新的套件版本。如需詳細資訊,請參閱 npm-install 文件。
將
scripts檔案的package.json屬性取代為下列程式碼:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },已定義下列指令碼:
-
build:在開發模式下統合用戶端資源,並監看檔案變更。 檔案監看員會導致套件組合在每次專案檔變更時重新產生。mode選項會停用生產環境最佳化,例如樹狀結構搖晃和縮製。build僅限用於開發。 -
release:在生產模式下統合您的用戶端資源。 -
publish:執行release指令碼,以在生產模式下組合用戶端資源。 它會呼叫 .NET CLI 的 publish 命令以發佈應用程式。
-
使用下列程式碼,在專案根目錄中建立名為
webpack.config.js的檔案:const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, "wwwroot"), filename: "[name].[chunkhash].js", publicPath: "/", }, resolve: { extensions: [".js", ".ts"], }, module: { rules: [ { test: /\.ts$/, use: "ts-loader", }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./src/index.html", }), new MiniCssExtractPlugin({ filename: "css/[name].[chunkhash].css", }), ], };上述檔案會設定 Webpack 編譯程序:
-
output屬性會覆寫dist的預設值。 套件組合會轉而在wwwroot目錄中發出。 -
resolve.extensions陣列包含.js以匯入 SignalR 用戶端 JavaScript。
-
在專案根目錄
src中,為用戶端程式碼建立名為SignalRWebpack/的新目錄。將
src目錄及其內容從範例專案複製到專案根目錄中。src目錄包含下列檔案:index.html,會定義首頁的樣板標記:<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>ASP.NET Core SignalR with TypeScript and Webpack</title> </head> <body> <div id="divMessages" class="messages"></div> <div class="input-zone"> <label id="lblMessage" for="tbMessage">Message:</label> <input id="tbMessage" class="input-zone-input" type="text" /> <button id="btnSend">Send</button> </div> </body> </html>css/main.css,會提供首頁的 CSS 樣式:*, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; padding: 0; } .input-zone { align-items: center; display: flex; flex-direction: row; margin: 10px; } .input-zone-input { flex: 1; margin-right: 10px; } .message-author { font-weight: bold; } .messages { border: 1px solid #000; margin: 10px; max-height: 300px; min-height: 300px; overflow-y: auto; padding: 5px; }tsconfig.json,會設定 TypeScript 編譯器以產生 ECMAScript 5 相容的 JavaScript:{ "compilerOptions": { "target": "es5" } }index.ts:import * as signalR from "@microsoft/signalr"; import "./css/main.css"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { const m = document.createElement("div"); m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(m); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch((err) => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.key === "Enter") { send(); } }); btnSend.addEventListener("click", send); function send() { connection.send("newMessage", username, tbMessage.value) .then(() => (tbMessage.value = "")); }上述程式碼會擷取 DOM 元素的參考,並將附加兩個事件處理常式:
-
keyup:在使用者輸入tbMessage文字方塊時引發,並且在使用者按send鍵時呼叫 函式。 -
click:在使用者選取 [傳送] 按鈕並呼叫send函式時引發。
HubConnectionBuilder類別會建立用於設定伺服器連線的新產生器。withUrl函式則會設定中樞 URL。SignalR 可讓用戶端與伺服器之間進行訊息交換。 每個訊息都有特定的名稱。 例如,名稱為
messageReceived的訊息可以執行負責在訊息區域中顯示新訊息的邏輯。 接聽特定的訊息可透過on函式完成。 可以接聽任意數目的訊息名稱。 也可將參數傳遞給訊息,例如作者的名稱和已接收訊息的內容。 用戶端收到訊息之後,就會在其div屬性中使用作者的名稱和訊息內容建立新的innerHTML元素。 它會新增至顯示訊息的主要div元素。透過 Websocket 連線傳送訊息需要呼叫
send方法。 方法的第一個參數是訊息名稱。 訊息資料則佔用其他參數。 在此範例中,識別為newMessage的訊息會傳送到伺服器。 訊息是由使用者名稱和文字方塊中的使用者輸入所組成。 如果傳送可正常運作,則會清除文字方塊的值。-
在專案根目錄下執行下列命令:
npm i @microsoft/signalr @types/node上述命令會安裝:
- SignalR TypeScript 用戶端,可讓用戶端將訊息傳送至伺服器。
- Node.js的 TypeScript 類型定義,可啟用 Node.js 類型的編譯時間檢查。
測試應用程式
使用下列步驟確認應用程式可正常運作:
以
release模式執行 Webpack。 使用 [套件管理員主控台] 視窗,在專案根目錄中執行下列命令。npm run release此命令會產生執行應用程式時要提供的用戶端資產。 這些資產位於
wwwroot資料夾中。Webpack 已完成下列工作:
- 清除
wwwroot目錄的內容。 - 在名為轉譯的程序中將 TypeScript 轉換為 JavaScript。
- 在名為縮減的程序中,破壞產生的 JavaScript 以縮減檔案大小。
- 將處理的 JavaScript、CSS 與 HTML 檔案從
src複製到wwwroot目錄。 - 將下列元素插入
wwwroot/index.html檔案中:- 一個
<link>標籤,參考wwwroot/main.<hash>.css檔案。 此標記緊接在結尾</head>標記之前。 - 一個
<script>標籤,參考已縮減的wwwroot/main.<hash>.js檔案。 此標籤緊接在</title>結尾標籤後面。
- 一個
- 清除
選取 [偵錯]>[啟動但不偵錯],啟動瀏覽器中的應用程式,但不附加偵錯工具。
wwwroot/index.html檔案會提供於https://localhost:<port>。如果發生編譯錯誤,請嘗試將解決方案關閉再重新開啟。
開啟另一個瀏覽器執行個體 (任何瀏覽器),並在網址列中貼上 URL。
選擇其中一個瀏覽器,在 [訊息] 文字方塊中鍵入某些內容,然後選取 [傳送] 按鈕。 唯一使用者名稱和訊息會立即顯示在兩個頁面上。
後續步驟
其他資源
本教學課程示範如何在 ASP.NET Core Web 應用程式中使用 SignalR,以統合及建置以 TypeScript 撰寫的用戶端。 Webpack 可讓開發人員組合並建置 Web 應用程式的用戶端資源。
在本教學課程中,您會了解如何:
- 建立 ASP.NET Core SignalR 應用程式
- 設定 SignalR 伺服器
- 使用 Webpack 設定組建管線
- 設定 SignalR TypeScript 用戶端
- 啟用用戶端與伺服器之間的通訊
檢視或下載範例程式碼 \(英文\) (如何下載)
Prerequisites
Visual Studio 2022 和 ASP.NET 與 Web 開發工作負載。
建立 ASP.NET Core Web 應用程式
根據預設,Visual Studio 會使用在其安裝目錄中找到的 npm 版本。 若要設定 Visual Studio 以在 PATH 環境變數中尋找 npm:
啟動 Visual Studio。 在 [開始] 視窗中,選取 [不使用程式碼繼續]。
巡覽至 [工具]>[選項]>[專案和方案]>[網頁套件管理]>[外部 Web 工具]。
從清單中選取
$(PATH)項目。 選取向上箭號,將此項目移至清單中的第二個位置,然後選取 [確定]:
.
若要建立新的 ASP.NET Core Web 應用程式:
- 使用 [檔案]>[新增]>[專案] 功能表選項,然後選擇 [空的 ASP.NET Core] 範本。 選取 下一步。
- 將專案命名為
SignalRWebpack,然後選取 [建立]。 - 從 [架構] 下拉式清單中選取 [.NET 8.0 (長期支援)]。 選取 ,創建。
將 Microsoft.TypeScript.MSBuild NuGet 套件新增至專案:
- 在方案總管中,以滑鼠右鍵按一下專案節點,然後選取 [管理 NuGet 套件]。 在 [瀏覽] 索引標籤中搜尋
Microsoft.TypeScript.MSBuild,然後選取右側的 [安裝] 以安裝套件。
Visual Studio 會在方案總管的 [相依性] 節點底下新增 NuGet 套件,而在專案中啟用 TypeScript 編譯。
設定伺服器
在本節中,您會設定 ASP.NET Core Web 應用程式以傳送和接收 SignalR 訊息。
在
Program.cs中,呼叫 AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();同樣地,在
Program.cs中呼叫 UseDefaultFiles 和 UseStaticFiles:var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles();上述程式碼可讓伺服器找出並提供
index.html檔案。 無論使用者輸入檔案的完整 URL 還是 Web 應用程式的根 URL,檔案都會提供。在專案根目錄
Hubs中,為SignalRWebpack/中樞類別建立名為 SignalR 的新目錄。使用下列程式碼建立新檔案
Hubs/ChatHub.cs:using Microsoft.AspNetCore.SignalR; namespace SignalRWebpack.Hubs; public class ChatHub : Hub { public async Task NewMessage(long username, string message) => await Clients.All.SendAsync("messageReceived", username, message); }上述程式碼會在伺服器收到訊息之後,向所有連線的使用者廣播已接收的訊息。 不需要讓泛型
on方法接收所有訊息。 以訊息名稱命名方法就已足夠。在此範例中:
- TypeScript 用戶端會傳送一則識別為
newMessage的訊息。 - C#
NewMessage方法預期有用戶端所傳送的資料。 - 在 SendAsync 上呼叫 。
- 接收的訊息就會傳送到連線至中樞的所有用戶端。
- TypeScript 用戶端會傳送一則識別為
在
using頂端新增下列Program.cs陳述式,以解析ChatHub參考:using SignalRWebpack.Hubs;在
Program.cs中,將/hub路由對應至ChatHub中樞。 將顯示Hello World!的程式碼取代為下列程式碼:app.MapHub<ChatHub>("/hub");
設定用戶端
在本節中,您會建立 Node.js 專案,以使用Webpack將 TypeScript 轉換成 JavaScript,並配套客戶端資源,包括 HTML 和 CSS。
在專案根目錄中執行下列命令,以建立
package.json檔案:npm init -y將醒目提示的屬性新增至
package.json檔案,並儲存檔案變更:{ "name": "SignalRWebpack", "version": "1.0.0", "private": true, "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }將
private屬性設定為true可避免在下一個步驟中出現套件安裝警告。安裝必要的 npm 套件。 從專案根目錄執行下列命令︰
npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli-E選項會停用 npm 將語意版本控制範圍運算子寫入package.json的預設行為。 例如,會使用"webpack": "5.76.1",而不是"webpack": "^5.76.1"。 此選項可防止意外升級至較新的套件版本。如需詳細資訊,請參閱 npm-install 文件。
將
scripts檔案的package.json屬性取代為下列程式碼:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },已定義下列指令碼:
-
build:在開發模式下統合用戶端資源,並監看檔案變更。 檔案監看員會導致套件組合在每次專案檔變更時重新產生。mode選項會停用生產環境最佳化,例如樹狀結構搖晃和縮製。build僅限用於開發。 -
release:在生產模式下統合您的用戶端資源。 -
publish:執行release指令碼,以在生產模式下組合用戶端資源。 它會呼叫 .NET CLI 的 publish 命令以發佈應用程式。
-
使用下列程式碼,在專案根目錄中建立名為
webpack.config.js的檔案:const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, "wwwroot"), filename: "[name].[chunkhash].js", publicPath: "/", }, resolve: { extensions: [".js", ".ts"], }, module: { rules: [ { test: /\.ts$/, use: "ts-loader", }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./src/index.html", }), new MiniCssExtractPlugin({ filename: "css/[name].[chunkhash].css", }), ], };上述檔案會設定 Webpack 編譯程序:
-
output屬性會覆寫dist的預設值。 套件組合會轉而在wwwroot目錄中發出。 -
resolve.extensions陣列包含.js以匯入 SignalR 用戶端 JavaScript。
-
在專案根目錄
src中,為用戶端程式碼建立名為SignalRWebpack/的新目錄。將
src目錄及其內容從範例專案複製到專案根目錄中。src目錄包含下列檔案:index.html,會定義首頁的樣板標記:<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>ASP.NET Core SignalR with TypeScript and Webpack</title> </head> <body> <div id="divMessages" class="messages"></div> <div class="input-zone"> <label id="lblMessage" for="tbMessage">Message:</label> <input id="tbMessage" class="input-zone-input" type="text" /> <button id="btnSend">Send</button> </div> </body> </html>css/main.css,會提供首頁的 CSS 樣式:*, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; padding: 0; } .input-zone { align-items: center; display: flex; flex-direction: row; margin: 10px; } .input-zone-input { flex: 1; margin-right: 10px; } .message-author { font-weight: bold; } .messages { border: 1px solid #000; margin: 10px; max-height: 300px; min-height: 300px; overflow-y: auto; padding: 5px; }tsconfig.json,會設定 TypeScript 編譯器以產生 ECMAScript 5 相容的 JavaScript:{ "compilerOptions": { "target": "es5" } }index.ts:import * as signalR from "@microsoft/signalr"; import "./css/main.css"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { const m = document.createElement("div"); m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(m); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch((err) => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.key === "Enter") { send(); } }); btnSend.addEventListener("click", send); function send() { connection.send("newMessage", username, tbMessage.value) .then(() => (tbMessage.value = "")); }上述程式碼會擷取 DOM 元素的參考,並將附加兩個事件處理常式:
-
keyup:在使用者輸入tbMessage文字方塊時引發,並且在使用者按send鍵時呼叫 函式。 -
click:在使用者選取 [傳送] 按鈕並呼叫send函式時引發。
HubConnectionBuilder類別會建立用於設定伺服器連線的新產生器。withUrl函式則會設定中樞 URL。SignalR 可讓用戶端與伺服器之間進行訊息交換。 每個訊息都有特定的名稱。 例如,名稱為
messageReceived的訊息可以執行負責在訊息區域中顯示新訊息的邏輯。 接聽特定的訊息可透過on函式完成。 可以接聽任意數目的訊息名稱。 也可將參數傳遞給訊息,例如作者的名稱和已接收訊息的內容。 用戶端收到訊息之後,就會在其div屬性中使用作者的名稱和訊息內容建立新的innerHTML元素。 它會新增至顯示訊息的主要div元素。透過 Websocket 連線傳送訊息需要呼叫
send方法。 方法的第一個參數是訊息名稱。 訊息資料則佔用其他參數。 在此範例中,識別為newMessage的訊息會傳送到伺服器。 訊息是由使用者名稱和文字方塊中的使用者輸入所組成。 如果傳送可正常運作,則會清除文字方塊的值。-
在專案根目錄下執行下列命令:
npm i @microsoft/signalr @types/node上述命令會安裝:
- SignalR TypeScript 用戶端,可讓用戶端將訊息傳送至伺服器。
- Node.js的 TypeScript 類型定義,可啟用 Node.js 類型的編譯時間檢查。
測試應用程式
使用下列步驟確認應用程式可正常運作:
以
release模式執行 Webpack。 使用 [套件管理員主控台] 視窗,在專案根目錄中執行下列命令。npm run release此命令會產生執行應用程式時要提供的用戶端資產。 這些資產位於
wwwroot資料夾中。Webpack 已完成下列工作:
- 清除
wwwroot目錄的內容。 - 在名為轉譯的程序中將 TypeScript 轉換為 JavaScript。
- 在名為縮減的程序中,破壞產生的 JavaScript 以縮減檔案大小。
- 將處理的 JavaScript、CSS 與 HTML 檔案從
src複製到wwwroot目錄。 - 將下列元素插入
wwwroot/index.html檔案中:- 一個
<link>標籤,參考wwwroot/main.<hash>.css檔案。 此標記緊接在結尾</head>標記之前。 - 一個
<script>標籤,參考已縮減的wwwroot/main.<hash>.js檔案。 此標籤緊接在</title>結尾標籤後面。
- 一個
- 清除
選取 [偵錯]>[啟動但不偵錯],啟動瀏覽器中的應用程式,但不附加偵錯工具。
wwwroot/index.html檔案會提供於https://localhost:<port>。如果發生編譯錯誤,請嘗試將解決方案關閉再重新開啟。
開啟另一個瀏覽器執行個體 (任何瀏覽器),並在網址列中貼上 URL。
選擇其中一個瀏覽器,在 [訊息] 文字方塊中鍵入某些內容,然後選取 [傳送] 按鈕。 唯一使用者名稱和訊息會立即顯示在兩個頁面上。
後續步驟
其他資源
本教學課程示範如何在 ASP.NET Core Web 應用程式中使用 SignalR,以統合及建置以 TypeScript 撰寫的用戶端。 Webpack 可讓開發人員組合並建置 Web 應用程式的用戶端資源。
在本教學課程中,您會了解如何:
- 建立 ASP.NET Core SignalR 應用程式
- 設定 SignalR 伺服器
- 使用 Webpack 設定組建管線
- 設定 SignalR TypeScript 用戶端
- 啟用用戶端與伺服器之間的通訊
檢視或下載範例程式碼 \(英文\) (如何下載)
Prerequisites
Visual Studio 2022 和 ASP.NET 與 Web 開發工作負載。
建立 ASP.NET Core Web 應用程式
根據預設,Visual Studio 會使用在其安裝目錄中找到的 npm 版本。 若要設定 Visual Studio 以在 PATH 環境變數中尋找 npm:
啟動 Visual Studio。 在 [開始] 視窗中,選取 [不使用程式碼繼續]。
巡覽至 [工具]>[選項]>[專案和方案]>[網頁套件管理]>[外部 Web 工具]。
從清單中選取
$(PATH)項目。 選取向上箭號,將此項目移至清單中的第二個位置,然後選取 [確定]:
.
若要建立新的 ASP.NET Core Web 應用程式:
- 使用 [檔案]>[新增]>[專案] 功能表選項,然後選擇 [空的 ASP.NET Core] 範本。 選取 下一步。
- 將專案命名為
SignalRWebpack,然後選取 [建立]。 - 從 架構 下拉式清單中選取 .NET 7.0(標準期限支援)。 選取 ,創建。
將 Microsoft.TypeScript.MSBuild NuGet 套件新增至專案:
- 在方案總管中,以滑鼠右鍵按一下專案節點,然後選取 [管理 NuGet 套件]。 在 [瀏覽] 索引標籤中搜尋
Microsoft.TypeScript.MSBuild,然後選取右側的 [安裝] 以安裝套件。
Visual Studio 會在方案總管的 [相依性] 節點底下新增 NuGet 套件,而在專案中啟用 TypeScript 編譯。
設定伺服器
在本節中,您會設定 ASP.NET Core Web 應用程式以傳送和接收 SignalR 訊息。
在
Program.cs中,呼叫 AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();同樣地,在
Program.cs中呼叫 UseDefaultFiles 和 UseStaticFiles:var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles();上述程式碼可讓伺服器找出並提供
index.html檔案。 無論使用者輸入檔案的完整 URL 還是 Web 應用程式的根 URL,檔案都會提供。在專案根目錄
Hubs中,為SignalRWebpack/中樞類別建立名為 SignalR 的新目錄。使用下列程式碼建立新檔案
Hubs/ChatHub.cs:using Microsoft.AspNetCore.SignalR; namespace SignalRWebpack.Hubs; public class ChatHub : Hub { public async Task NewMessage(long username, string message) => await Clients.All.SendAsync("messageReceived", username, message); }上述程式碼會在伺服器收到訊息之後,向所有連線的使用者廣播已接收的訊息。 不需要讓泛型
on方法接收所有訊息。 以訊息名稱命名方法就已足夠。在此範例中:
- TypeScript 用戶端會傳送一則識別為
newMessage的訊息。 - C#
NewMessage方法預期有用戶端所傳送的資料。 - 在 SendAsync 上呼叫 。
- 接收的訊息就會傳送到連線至中樞的所有用戶端。
- TypeScript 用戶端會傳送一則識別為
在
using頂端新增下列Program.cs陳述式,以解析ChatHub參考:using SignalRWebpack.Hubs;在
Program.cs中,將/hub路由對應至ChatHub中樞。 將顯示Hello World!的程式碼取代為下列程式碼:app.MapHub<ChatHub>("/hub");
設定用戶端
在本節中,您會建立 Node.js 專案,以使用Webpack將 TypeScript 轉換成 JavaScript,並配套客戶端資源,包括 HTML 和 CSS。
在專案根目錄中執行下列命令,以建立
package.json檔案:npm init -y將醒目提示的屬性新增至
package.json檔案,並儲存檔案變更:{ "name": "SignalRWebpack", "version": "1.0.0", "private": true, "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }將
private屬性設定為true可避免在下一個步驟中出現套件安裝警告。安裝必要的 npm 套件。 從專案根目錄執行下列命令︰
npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli-E選項會停用 npm 將語意版本控制範圍運算子寫入package.json的預設行為。 例如,會使用"webpack": "5.76.1",而不是"webpack": "^5.76.1"。 此選項可防止意外升級至較新的套件版本。如需詳細資訊,請參閱 npm-install 文件。
將
scripts檔案的package.json屬性取代為下列程式碼:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },已定義下列指令碼:
-
build:在開發模式下統合用戶端資源,並監看檔案變更。 檔案監看員會導致套件組合在每次專案檔變更時重新產生。mode選項會停用生產環境最佳化,例如樹狀結構搖晃和縮製。build僅限用於開發。 -
release:在生產模式下統合您的用戶端資源。 -
publish:執行release指令碼,以在生產模式下組合用戶端資源。 它會呼叫 .NET CLI 的 publish 命令以發佈應用程式。
-
使用下列程式碼,在專案根目錄中建立名為
webpack.config.js的檔案:const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, "wwwroot"), filename: "[name].[chunkhash].js", publicPath: "/", }, resolve: { extensions: [".js", ".ts"], }, module: { rules: [ { test: /\.ts$/, use: "ts-loader", }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./src/index.html", }), new MiniCssExtractPlugin({ filename: "css/[name].[chunkhash].css", }), ], };上述檔案會設定 Webpack 編譯程序:
-
output屬性會覆寫dist的預設值。 套件組合會轉而在wwwroot目錄中發出。 -
resolve.extensions陣列包含.js以匯入 SignalR 用戶端 JavaScript。
-
將
src目錄及其內容從範例專案複製到專案根目錄中。src目錄包含下列檔案:index.html,會定義首頁的樣板標記:<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>ASP.NET Core SignalR with TypeScript and Webpack</title> </head> <body> <div id="divMessages" class="messages"></div> <div class="input-zone"> <label id="lblMessage" for="tbMessage">Message:</label> <input id="tbMessage" class="input-zone-input" type="text" /> <button id="btnSend">Send</button> </div> </body> </html>css/main.css,會提供首頁的 CSS 樣式:*, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; padding: 0; } .input-zone { align-items: center; display: flex; flex-direction: row; margin: 10px; } .input-zone-input { flex: 1; margin-right: 10px; } .message-author { font-weight: bold; } .messages { border: 1px solid #000; margin: 10px; max-height: 300px; min-height: 300px; overflow-y: auto; padding: 5px; }tsconfig.json,會設定 TypeScript 編譯器以產生 ECMAScript 5 相容的 JavaScript:{ "compilerOptions": { "target": "es5" } }index.ts:import * as signalR from "@microsoft/signalr"; import "./css/main.css"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { const m = document.createElement("div"); m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(m); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch((err) => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.key === "Enter") { send(); } }); btnSend.addEventListener("click", send); function send() { connection.send("newMessage", username, tbMessage.value) .then(() => (tbMessage.value = "")); }上述程式碼會擷取 DOM 元素的參考,並將附加兩個事件處理常式:
-
keyup:在使用者輸入tbMessage文字方塊時引發,並且在使用者按send鍵時呼叫 函式。 -
click:在使用者選取 [傳送] 按鈕並呼叫send函式時引發。
HubConnectionBuilder類別會建立用於設定伺服器連線的新產生器。withUrl函式則會設定中樞 URL。SignalR 可讓用戶端與伺服器之間進行訊息交換。 每個訊息都有特定的名稱。 例如,名稱為
messageReceived的訊息可以執行負責在訊息區域中顯示新訊息的邏輯。 接聽特定的訊息可透過on函式完成。 可以接聽任意數目的訊息名稱。 也可將參數傳遞給訊息,例如作者的名稱和已接收訊息的內容。 用戶端收到訊息之後,就會在其div屬性中使用作者的名稱和訊息內容建立新的innerHTML元素。 它會新增至顯示訊息的主要div元素。透過 Websocket 連線傳送訊息需要呼叫
send方法。 方法的第一個參數是訊息名稱。 訊息資料則佔用其他參數。 在此範例中,識別為newMessage的訊息會傳送到伺服器。 訊息是由使用者名稱和文字方塊中的使用者輸入所組成。 如果傳送可正常運作,則會清除文字方塊的值。-
在專案根目錄下執行下列命令:
npm i @microsoft/signalr @types/node上述命令會安裝:
- SignalR TypeScript 用戶端,可讓用戶端將訊息傳送至伺服器。
- Node.js的 TypeScript 類型定義,可啟用 Node.js 類型的編譯時間檢查。
測試應用程式
使用下列步驟確認應用程式可正常運作:
以
release模式執行 Webpack。 使用 [套件管理員主控台] 視窗,在專案根目錄中執行下列命令。npm run release此命令會產生執行應用程式時要提供的用戶端資產。 這些資產位於
wwwroot資料夾中。Webpack 已完成下列工作:
- 清除
wwwroot目錄的內容。 - 在名為轉譯的程序中將 TypeScript 轉換為 JavaScript。
- 在名為縮減的程序中,破壞產生的 JavaScript 以縮減檔案大小。
- 將處理的 JavaScript、CSS 與 HTML 檔案從
src複製到wwwroot目錄。 - 將下列元素插入
wwwroot/index.html檔案中:- 一個
<link>標籤,參考wwwroot/main.<hash>.css檔案。 此標記緊接在結尾</head>標記之前。 - 一個
<script>標籤,參考已縮減的wwwroot/main.<hash>.js檔案。 此標籤緊接在</title>結尾標籤後面。
- 一個
- 清除
選取 [偵錯]>[啟動但不偵錯],啟動瀏覽器中的應用程式,但不附加偵錯工具。
wwwroot/index.html檔案會提供於https://localhost:<port>。如果發生編譯錯誤,請嘗試將解決方案關閉再重新開啟。
開啟另一個瀏覽器執行個體 (任何瀏覽器),並在網址列中貼上 URL。
選擇其中一個瀏覽器,在 [訊息] 文字方塊中鍵入某些內容,然後選取 [傳送] 按鈕。 唯一使用者名稱和訊息會立即顯示在兩個頁面上。
後續步驟
其他資源
本教學課程示範如何在 ASP.NET Core Web 應用程式中使用 SignalR,以統合及建置以 TypeScript 撰寫的用戶端。 Webpack 可讓開發人員組合並建置 Web 應用程式的用戶端資源。
在本教學課程中,您會了解如何:
- 建立 ASP.NET Core SignalR 應用程式
- 設定 SignalR 伺服器
- 使用 Webpack 設定組建管線
- 設定 SignalR TypeScript 用戶端
- 啟用用戶端與伺服器之間的通訊
檢視或下載範例程式碼 \(英文\) (如何下載)
Prerequisites
- Visual Studio 2022 和 ASP.NET 與 Web 開發工作負載。
- .NET 6 SDK
建立 ASP.NET Core Web 應用程式
根據預設,Visual Studio 會使用在其安裝目錄中找到的 npm 版本。 若要設定 Visual Studio 以在 PATH 環境變數中尋找 npm:
啟動 Visual Studio。 在 [開始] 視窗中,選取 [不使用程式碼繼續]。
巡覽至 [工具]>[選項]>[專案和方案]>[網頁套件管理]>[外部 Web 工具]。
從清單中選取
$(PATH)項目。 選取向上箭號,將此項目移至清單中的第二個位置,然後選取 [確定]:
.
若要建立新的 ASP.NET Core Web 應用程式:
- 使用 [檔案]>[新增]>[專案] 功能表選項,然後選擇 [空的 ASP.NET Core] 範本。 選取 下一步。
- 將專案命名為
SignalRWebpack,然後選取 [建立]。 - 從 架構 下拉式清單中選取 .NET 6.0(長期支援)。 選取 ,創建。
將 Microsoft.TypeScript.MSBuild NuGet 套件新增至專案:
- 在方案總管中,以滑鼠右鍵按一下專案節點,然後選取 [管理 NuGet 套件]。 在 [瀏覽] 索引標籤中搜尋
Microsoft.TypeScript.MSBuild,然後選取右側的 [安裝] 以安裝套件。
Visual Studio 會在方案總管的 [相依性] 節點底下新增 NuGet 套件,而在專案中啟用 TypeScript 編譯。
設定伺服器
在本節中,您會設定 ASP.NET Core Web 應用程式以傳送和接收 SignalR 訊息。
在
Program.cs中,呼叫 AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();同樣地,在
Program.cs中呼叫 UseDefaultFiles 和 UseStaticFiles:var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles();上述程式碼可讓伺服器找出並提供
index.html檔案。 無論使用者輸入檔案的完整 URL 還是 Web 應用程式的根 URL,檔案都會提供。在專案根目錄
Hubs中,為SignalRWebpack/中樞類別建立名為 SignalR 的新目錄。使用下列程式碼建立新檔案
Hubs/ChatHub.cs:using Microsoft.AspNetCore.SignalR; namespace SignalRWebpack.Hubs; public class ChatHub : Hub { public async Task NewMessage(long username, string message) => await Clients.All.SendAsync("messageReceived", username, message); }上述程式碼會在伺服器收到訊息之後,向所有連線的使用者廣播已接收的訊息。 不需要讓泛型
on方法接收所有訊息。 以訊息名稱命名方法就已足夠。在此範例中,TypeScript 用戶端會傳送一則識別為
newMessage的訊息。 C#NewMessage方法預期有用戶端所傳送的資料。 在 SendAsync 上呼叫 。 接收的訊息就會傳送到連線至中樞的所有用戶端。在
using頂端新增下列Program.cs陳述式,以解析ChatHub參考:using SignalRWebpack.Hubs;在
Program.cs中,將/hub路由對應至ChatHub中樞。 將顯示Hello World!的程式碼取代為下列程式碼:app.MapHub<ChatHub>("/hub");
設定用戶端
在本節中,您會建立 Node.js 專案,以使用Webpack將 TypeScript 轉換成 JavaScript,並配套客戶端資源,包括 HTML 和 CSS。
在專案根目錄中執行下列命令,以建立
package.json檔案:npm init -y將醒目提示的屬性新增至
package.json檔案,並儲存檔案變更:{ "name": "SignalRWebpack", "version": "1.0.0", "private": true, "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }將
private屬性設定為true可避免在下一個步驟中出現套件安裝警告。安裝必要的 npm 套件。 從專案根目錄執行下列命令︰
npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli-E選項會停用 npm 將語意版本控制範圍運算子寫入package.json的預設行為。 例如,會使用"webpack": "5.70.0",而不是"webpack": "^5.70.0"。 此選項可防止意外升級至較新的套件版本。如需詳細資訊,請參閱 npm-install 文件。
將
scripts檔案的package.json屬性取代為下列程式碼:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },已定義下列指令碼:
-
build:在開發模式下統合用戶端資源,並監看檔案變更。 檔案監看員會導致套件組合在每次專案檔變更時重新產生。mode選項會停用生產環境最佳化,例如樹狀結構搖晃和縮製。build僅限用於開發。 -
release:在生產模式下統合您的用戶端資源。 -
publish:執行release指令碼,以在生產模式下組合用戶端資源。 它會呼叫 .NET CLI 的 publish 命令以發佈應用程式。
-
使用下列程式碼,在專案根目錄中建立名為
webpack.config.js的檔案:const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, "wwwroot"), filename: "[name].[chunkhash].js", publicPath: "/", }, resolve: { extensions: [".js", ".ts"], }, module: { rules: [ { test: /\.ts$/, use: "ts-loader", }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, ], }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./src/index.html", }), new MiniCssExtractPlugin({ filename: "css/[name].[chunkhash].css", }), ], };上述檔案會設定 Webpack 編譯程序:
-
output屬性會覆寫dist的預設值。 套件組合會轉而在wwwroot目錄中發出。 -
resolve.extensions陣列包含.js以匯入 SignalR 用戶端 JavaScript。
-
將
src目錄及其內容從範例專案複製到專案根目錄中。src目錄包含下列檔案:index.html,會定義首頁的樣板標記:<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>ASP.NET Core SignalR with TypeScript and Webpack</title> </head> <body> <div id="divMessages" class="messages"></div> <div class="input-zone"> <label id="lblMessage" for="tbMessage">Message:</label> <input id="tbMessage" class="input-zone-input" type="text" /> <button id="btnSend">Send</button> </div> </body> </html>css/main.css,會提供首頁的 CSS 樣式:*, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; padding: 0; } .input-zone { align-items: center; display: flex; flex-direction: row; margin: 10px; } .input-zone-input { flex: 1; margin-right: 10px; } .message-author { font-weight: bold; } .messages { border: 1px solid #000; margin: 10px; max-height: 300px; min-height: 300px; overflow-y: auto; padding: 5px; }tsconfig.json,會設定 TypeScript 編譯器以產生 ECMAScript 5 相容的 JavaScript:{ "compilerOptions": { "target": "es5" } }index.ts:import * as signalR from "@microsoft/signalr"; import "./css/main.css"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { const m = document.createElement("div"); m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(m); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch((err) => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.key === "Enter") { send(); } }); btnSend.addEventListener("click", send); function send() { connection.send("newMessage", username, tbMessage.value) .then(() => (tbMessage.value = "")); }
上述程式碼會擷取 DOM 元素的參考,並將附加兩個事件處理常式:
-
keyup:在使用者輸入tbMessage文字方塊時引發,並且在使用者按send鍵時呼叫 函式。 -
click:在使用者選取 [傳送] 按鈕並呼叫send函式時引發。
HubConnectionBuilder類別會建立用於設定伺服器連線的新產生器。withUrl函式則會設定中樞 URL。SignalR 可讓用戶端與伺服器之間進行訊息交換。 每個訊息都有特定的名稱。 例如,名稱為
messageReceived的訊息可以執行負責在訊息區域中顯示新訊息的邏輯。 接聽特定的訊息可透過on函式完成。 可以接聽任意數目的訊息名稱。 也可將參數傳遞給訊息,例如作者的名稱和已接收訊息的內容。 用戶端收到訊息之後,就會在其div屬性中使用作者的名稱和訊息內容建立新的innerHTML元素。 它會新增至顯示訊息的主要div元素。透過 Websocket 連線傳送訊息需要呼叫
send方法。 方法的第一個參數是訊息名稱。 訊息資料則佔用其他參數。 在此範例中,識別為newMessage的訊息會傳送到伺服器。 訊息是由使用者名稱和文字方塊中的使用者輸入所組成。 如果傳送可正常運作,則會清除文字方塊的值。在專案根目錄下執行下列命令:
npm i @microsoft/signalr @types/node上述命令會安裝:
- SignalR TypeScript 用戶端,可讓用戶端將訊息傳送至伺服器。
- Node.js的 TypeScript 類型定義,可啟用 Node.js 類型的編譯時間檢查。
測試應用程式
使用下列步驟確認應用程式可正常運作:
以
release模式執行 Webpack。 使用 [套件管理員主控台] 視窗,在專案根目錄中執行下列命令。 如果您不在專案根目錄中,請先輸入cd SignalRWebpack,再輸入命令。npm run release此命令會產生執行應用程式時要提供的用戶端資產。 這些資產位於
wwwroot資料夾中。Webpack 已完成下列工作:
- 清除
wwwroot目錄的內容。 - 在名為轉譯的程序中將 TypeScript 轉換為 JavaScript。
- 在名為縮減的程序中,破壞產生的 JavaScript 以縮減檔案大小。
- 將處理的 JavaScript、CSS 與 HTML 檔案從
src複製到wwwroot目錄。 - 將下列元素插入
wwwroot/index.html檔案中:- 一個
<link>標籤,參考wwwroot/main.<hash>.css檔案。 此標記緊接在結尾</head>標記之前。 - 一個
<script>標籤,參考已縮減的wwwroot/main.<hash>.js檔案。 此標籤緊接在</title>結尾標籤後面。
- 一個
- 清除
選取 [偵錯]>[啟動但不偵錯],啟動瀏覽器中的應用程式,但不附加偵錯工具。
wwwroot/index.html檔案會提供於https://localhost:<port>。如果發生編譯錯誤,請嘗試將解決方案關閉再重新開啟。
開啟另一個瀏覽器執行個體 (任何瀏覽器),並在網址列中貼上 URL。
選擇其中一個瀏覽器,在 [訊息] 文字方塊中鍵入某些內容,然後選取 [傳送] 按鈕。 唯一使用者名稱和訊息會立即顯示在兩個頁面上。
後續步驟
其他資源
本教學課程示範如何在 ASP.NET Core Web 應用程式中使用 SignalR,以統合及建置以 TypeScript 撰寫的用戶端。 Webpack 可讓開發人員組合並建置 Web 應用程式的用戶端資源。
在本教學課程中,您會了解如何:
- 建構入門 ASP.NET Core SignalR 應用程式
- 設定 SignalR TypeScript 用戶端
- 使用 Webpack 設定組建管線
- 設定 SignalR 伺服器
- 啟用用戶端與伺服器之間的通訊
檢視或下載範例程式碼 \(英文\) (如何下載)
Prerequisites
- Visual Studio 2019 和 ASP.NET 與 Web 開發工作負載
- .NET Core SDK 3.0 或更新版本
- Node.js 使用 npm
建立 ASP.NET Core Web 應用程式
設定 Visual Studio 以在 PATH 環境變數中尋找 npm。 根據預設,Visual Studio 會使用在其安裝目錄中找到的 npm 版本。 請遵循 Visual Studio 中的下列指示:
啟動 Visual Studio。 在 [開始] 視窗中,選取 [不使用程式碼繼續]。
巡覽至 [工具]>[選項]>[專案和方案]>[網頁套件管理]>[外部 Web 工具]。
從清單中選取 $(PATH) 項目。 選取向上箭號,將此項目移至清單中的第二個位置,然後選取 [確定]。
.
Visual Studio 設定完成。
- 使用 [檔案]>[新增]>[專案] 功能表選項,然後選擇 [ASP.NET Core Web 應用程式] 範本。 選取 下一步。
- 將專案命名為 *SignalRWebPac``,然後選取 [建立]。
- 從目標 Framework 下拉式清單中選取 [.NET Core],然後從 Framework 選取器下拉式清單中選取 [ASP.NET Core 3.1]。 選取 [空白] 範本,然後選取 [建立]。
將 Microsoft.TypeScript.MSBuild 套件新增至專案:
- 在方案總管 (右窗格) 中,以滑鼠右鍵按一下專案節點,然後選取 [管理 NuGet 套件]。 在 [瀏覽] 索引標籤中搜尋
Microsoft.TypeScript.MSBuild,然後按一下右側的 [安裝] 以安裝套件。
Visual Studio 會在方案總管的 [相依性] 節點底下新增 NuGet 套件,而在專案中啟用 TypeScript 編譯。
設定 Webpack 和 TypeScript
下列步驟可設定 TypeScript 至 JavaScript 的轉換和用戶端資源的組合。
在專案根目錄中執行下列命令,以建立
package.json檔案:npm init -y將醒目提示的屬性新增至
package.json檔案,並儲存檔案變更:{ "name": "SignalRWebPack", "version": "1.0.0", "private": true, "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }將
private屬性設定為true可避免在下一個步驟中出現套件安裝警告。安裝必要的 npm 套件。 從專案根目錄執行下列命令︰
npm i -D -E clean-webpack-plugin@3.0.0 css-loader@3.4.2 html-webpack-plugin@3.2.0 mini-css-extract-plugin@0.9.0 ts-loader@6.2.1 typescript@3.7.5 webpack@4.41.5 webpack-cli@3.3.10要注意的一些命令詳細資料:
- 版本號碼接在每一個套件名稱的
@符號之後。 npm 會安裝這些特定的套件版本。 -
-E選項會停用 npm 將語意版本控制範圍運算子寫入 *packagejson的預設行為。 例如,會使用"webpack": "4.41.5",而不是"webpack": "^4.41.5"。 此選項可防止意外升級至較新的套件版本。
如需詳細資訊,請參閱 npm-install 文件。
- 版本號碼接在每一個套件名稱的
將
scripts檔案的package.json屬性取代為下列程式碼:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },指令碼的一些說明:
-
build:在開發模式下統合用戶端資源,並監看檔案變更。 檔案監看員會導致套件組合在每次專案檔變更時重新產生。mode選項會停用生產環境最佳化,例如樹狀結構搖晃和縮製。build僅用於開發。 -
release:在生產模式下統合您的用戶端資源。 -
publish:執行release指令碼,以在生產模式下組合用戶端資源。 它會呼叫 .NET CLI 的 publish 命令以發佈應用程式。
-
使用下列程式碼,在專案根目錄中建立名為
webpack.config.js的檔案:const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, "wwwroot"), filename: "[name].[chunkhash].js", publicPath: "/" }, resolve: { extensions: [".js", ".ts"] }, module: { rules: [ { test: /\.ts$/, use: "ts-loader" }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./src/index.html" }), new MiniCssExtractPlugin({ filename: "css/[name].[chunkhash].css" }) ] };上述檔案會設定 Webpack 編譯。 要注意的一些組態詳細資料:
-
output屬性會覆寫dist的預設值。 套件組合會轉而在wwwroot目錄中發出。 -
resolve.extensions陣列包含.js以匯入 SignalR 用戶端 JavaScript。
-
在專案根目錄中建立新的 src 目錄,用以儲存專案的用戶端資產。
使用下列標記建立
src/index.html。<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>ASP.NET Core SignalR</title> </head> <body> <div id="divMessages" class="messages"> </div> <div class="input-zone"> <label id="lblMessage" for="tbMessage">Message:</label> <input id="tbMessage" class="input-zone-input" type="text" /> <button id="btnSend">Send</button> </div> </body> </html>上述的 HTML 會定義首頁的樣板標記。
建立新的 src/css 目錄。 其目的是要儲存專案的
.css檔案。使用下列 CSS 建立
src/css/main.css:*, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; padding: 0; } .input-zone { align-items: center; display: flex; flex-direction: row; margin: 10px; } .input-zone-input { flex: 1; margin-right: 10px; } .message-author { font-weight: bold; } .messages { border: 1px solid #000; margin: 10px; max-height: 300px; min-height: 300px; overflow-y: auto; padding: 5px; }上述
main.css檔案會設定應用程式的樣式。以下列 JSON 來建立
src/tsconfig.json:{ "compilerOptions": { "target": "es5" } }上述程式碼會設定 TypeScript 編譯器來產生 ECMAScript 5 相容的 JavaScript。
使用下列程式碼建立
src/index.ts:import "./css/main.css"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.key === "Enter") { send(); } }); btnSend.addEventListener("click", send); function send() { }上述的 TypeScript 會擷取 DOM 項目的參考,並將附加兩個事件處理常式:
-
keyup:此事件會在使用者輸入tbMessage文字方塊時引發。 當使用者按下send鍵時,即會呼叫 函式。 -
click:此事件會在使用者選取 [傳送] 按鈕時引發。 系統會呼叫send函式。
-
設定應用程式
在
Startup.Configure中,新增對 UseDefaultFiles(IApplicationBuilder) 和 UseStaticFiles(IApplicationBuilder) 的呼叫。public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseDefaultFiles(); app.UseStaticFiles(); app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/hub"); }); }上述程式碼可讓伺服器找出並提供
index.html檔案。 無論使用者輸入檔案的完整 URL 還是 Web 應用程式的根 URL,檔案都會提供。在
Startup.Configure的結尾,將 /hub 路由對應至ChatHub中樞。 將顯示 Hello World! 的程式碼取代為以下這一行:app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/hub"); });在
Startup.ConfigureServices中,呼叫 AddSignalR。services.AddSignalR();在專案根目錄 SignalRWebPack/ 中建立名為 Hubs 的新目錄,用以儲存 SignalR 中樞。
使用下列程式碼建立中樞
Hubs/ChatHub.cs:using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRWebPack.Hubs { public class ChatHub : Hub { } }在
using檔案的頂端新增下列Startup.cs陳述式,以解析ChatHub參考:using SignalRWebPack.Hubs;
啟用用戶端與伺服器的通訊
應用程式目前顯示用來傳送訊息的基本表單,但尚未正常運作。 伺服器正在接聽特定的路由,但對於已傳送的訊息不會進行任何處理。
在專案根目錄下執行下列命令:
npm i @microsoft/signalr @types/node上述命令會安裝:
- SignalR TypeScript 用戶端,可讓用戶端將訊息傳送至伺服器。
- Node.js的 TypeScript 類型定義,可啟用 Node.js 類型的編譯時間檢查。
將醒目提示的程式碼新增至
src/index.ts檔案:import "./css/main.css"; import * as signalR from "@microsoft/signalr"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { let m = document.createElement("div"); m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(m); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch(err => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.key === "Enter") { send(); } }); btnSend.addEventListener("click", send); function send() { }上述程式碼支援從伺服器接收訊息。
HubConnectionBuilder類別會建立用於設定伺服器連線的新產生器。withUrl函式則會設定中樞 URL。SignalR 可讓用戶端與伺服器之間進行訊息交換。 每個訊息都有特定的名稱。 例如,名稱為
messageReceived的訊息可以執行負責在訊息區域中顯示新訊息的邏輯。 接聽特定的訊息可透過on函式完成。 可以接聽任意數目的訊息名稱。 也可將參數傳遞給訊息,例如作者的名稱和已接收訊息的內容。 用戶端收到訊息之後,就會在其div屬性中使用作者的名稱和訊息內容建立新的innerHTML元素。 它會新增至顯示訊息的主要div元素。現在用戶端可以接收訊息,請設定它來傳送訊息。 將醒目提示的程式碼新增至
src/index.ts檔案:import "./css/main.css"; import * as signalR from "@microsoft/signalr"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { let messages = document.createElement("div"); messages.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(messages); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch(err => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.key === "Enter") { send(); } }); btnSend.addEventListener("click", send); function send() { connection.send("newMessage", username, tbMessage.value) .then(() => tbMessage.value = ""); }透過 Websocket 連線傳送訊息需要呼叫
send方法。 方法的第一個參數是訊息名稱。 訊息資料則佔用其他參數。 在此範例中,識別為newMessage的訊息會傳送到伺服器。 訊息是由使用者名稱和文字方塊中的使用者輸入所組成。 如果傳送可正常運作,則會清除文字方塊的值。將
NewMessage方法新增至ChatHub類別:using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRWebPack.Hubs { public class ChatHub : Hub { public async Task NewMessage(long username, string message) { await Clients.All.SendAsync("messageReceived", username, message); } } }上述程式碼會在伺服器收到訊息之後,向所有連線的使用者廣播已接收的訊息。 不需要讓泛型
on方法接收所有訊息。 以訊息名稱命名方法就已足夠。在此範例中,TypeScript 用戶端會傳送一則識別為
newMessage的訊息。 C#NewMessage方法預期有用戶端所傳送的資料。 在 SendAsync 上呼叫 。 接收的訊息就會傳送到連線至中樞的所有用戶端。
測試應用程式
使用下列步驟確認應用程式可正常運作。
在 release 模式下執行 Webpack。 使用 [套件管理員主控台] 視窗,在專案根目錄中執行下列命令。 如果您不在專案根目錄中,請先輸入
cd SignalRWebPack,再輸入命令。npm run release此命令會產生執行應用程式時要提供的用戶端資產。 這些資產位於
wwwroot資料夾中。Webpack 已完成下列工作:
- 清除
wwwroot目錄的內容。 - 在名為轉譯的程序中將 TypeScript 轉換為 JavaScript。
- 在名為縮減的程序中,破壞產生的 JavaScript 以縮減檔案大小。
- 將處理的 JavaScript、CSS 與 HTML 檔案從
src複製到wwwroot目錄。 - 將下列元素插入
wwwroot/index.html檔案中:- 一個
<link>標籤,參考wwwroot/main.<hash>.css檔案。 此標記緊接在結尾</head>標記之前。 - 一個
<script>標籤,參考已縮減的wwwroot/main.<hash>.js檔案。 此標籤緊接在</title>結尾標籤後面。
- 一個
- 清除
選取 [偵錯]>[啟動但不偵錯],啟動瀏覽器中的應用程式,但不附加偵錯工具。
wwwroot/index.html檔案會提供於http://localhost:<port_number>。如果發生編譯錯誤,請嘗試將解決方案關閉再重新開啟。
開啟另一個瀏覽器執行個體 (任何瀏覽器)。 在網址列中貼上 URL。
選擇其中一個瀏覽器,在 [訊息] 文字方塊中鍵入某些內容,然後選取 [傳送] 按鈕。 唯一使用者名稱和訊息會立即顯示在兩個頁面上。
其他資源
本教學課程示範如何在 ASP.NET Core Web 應用程式中使用 SignalR,以統合及建置以 TypeScript 撰寫的用戶端。 Webpack 可讓開發人員組合並建置 Web 應用程式的用戶端資源。
在本教學課程中,您會了解如何:
- 建構入門 ASP.NET Core SignalR 應用程式
- 設定 SignalR TypeScript 用戶端
- 使用 Webpack 設定組建管線
- 設定 SignalR 伺服器
- 啟用用戶端與伺服器之間的通訊
檢視或下載範例程式碼 \(英文\) (如何下載)
Prerequisites
- Visual Studio 2019 和 ASP.NET 與 Web 開發工作負載
- .NET Core SDK 2.2 或更新版本
- Node.js 使用 npm
建立 ASP.NET Core Web 應用程式
設定 Visual Studio 以在 PATH 環境變數中尋找 npm。 根據預設,Visual Studio 會使用在其安裝目錄中找到的 npm 版本。 請遵循 Visual Studio 中的下列指示:
巡覽至 [工具]>[選項]>[專案和方案]>[網頁套件管理]>[外部 Web 工具]。
從清單中選取 $(PATH) 項目。 選取向上箭號,將此項目移至清單中的第二個位置。
Visual Studio 組態已完成。 現在即可開始建立專案。
- 使用 [檔案]>[新增]>[專案] 功能表選項,然後選擇 [ASP.NET Core Web 應用程式] 範本。
- 將專案命名為 *SignalRWebPack`,然後選取 [建立]。
- 從目標 Framework 下拉式清單中選取 [.NET Core],然後從 Framework 選取器下拉式清單中選取 [ASP.NET Core 2.2]。 選取 [空白] 範本,然後選取 [建立]。
設定 Webpack 和 TypeScript
下列步驟可設定 TypeScript 至 JavaScript 的轉換和用戶端資源的組合。
在專案根目錄中執行下列命令,以建立
package.json檔案:npm init -y將醒目提示的屬性新增至
package.json檔案:{ "name": "SignalRWebPack", "version": "1.0.0", "private": true, "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }將
private屬性設定為true可避免在下一個步驟中出現套件安裝警告。安裝必要的 npm 套件。 從專案根目錄執行下列命令︰
npm install -D -E clean-webpack-plugin@1.0.1 css-loader@2.1.0 html-webpack-plugin@4.0.0-beta.5 mini-css-extract-plugin@0.5.0 ts-loader@5.3.3 typescript@3.3.3 webpack@4.29.3 webpack-cli@3.2.3要注意的一些命令詳細資料:
- 版本號碼接在每一個套件名稱的
@符號之後。 npm 會安裝這些特定的套件版本。 -
-E選項會停用 npm 將語意版本控制範圍運算子寫入 *packagejson的預設行為。 例如,會使用"webpack": "4.29.3",而不是"webpack": "^4.29.3"。 此選項可防止意外升級至較新的套件版本。
如需詳細資訊,請參閱 npm-install 文件。
- 版本號碼接在每一個套件名稱的
將
scripts檔案的package.json屬性取代為下列程式碼:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },指令碼的一些說明:
-
build:在開發模式下統合用戶端資源,並監看檔案變更。 檔案監看員會導致套件組合在每次專案檔變更時重新產生。mode選項會停用生產環境最佳化,例如樹狀結構搖晃和縮製。build僅用於開發。 -
release:在生產模式下統合您的用戶端資源。 -
publish:執行release指令碼,以在生產模式下組合用戶端資源。 它會呼叫 .NET CLI 的 publish 命令以發佈應用程式。
-
使用下列程式碼,在專案根目錄中建立名為
*webpack.config.js的檔案:const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const CleanWebpackPlugin = require("clean-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, "wwwroot"), filename: "[name].[chunkhash].js", publicPath: "/" }, resolve: { extensions: [".js", ".ts"] }, module: { rules: [ { test: /\.ts$/, use: "ts-loader" }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] } ] }, plugins: [ new CleanWebpackPlugin(["wwwroot/*"]), new HtmlWebpackPlugin({ template: "./src/index.html" }), new MiniCssExtractPlugin({ filename: "css/[name].[chunkhash].css" }) ] };上述檔案會設定 Webpack 編譯。 要注意的一些組態詳細資料:
-
output屬性會覆寫dist的預設值。 套件組合會轉而在wwwroot目錄中發出。 -
resolve.extensions陣列包含.js以匯入 SignalR 用戶端 JavaScript。
-
在專案根目錄中建立新的 src 目錄,用以儲存專案的用戶端資產。
使用下列標記建立
src/index.html。<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>ASP.NET Core SignalR</title> </head> <body> <div id="divMessages" class="messages"> </div> <div class="input-zone"> <label id="lblMessage" for="tbMessage">Message:</label> <input id="tbMessage" class="input-zone-input" type="text" /> <button id="btnSend">Send</button> </div> </body> </html>上述的 HTML 會定義首頁的樣板標記。
建立新的 src/css 目錄。 其目的是要儲存專案的
.css檔案。使用下列標記建立
src/css/main.css:*, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; padding: 0; } .input-zone { align-items: center; display: flex; flex-direction: row; margin: 10px; } .input-zone-input { flex: 1; margin-right: 10px; } .message-author { font-weight: bold; } .messages { border: 1px solid #000; margin: 10px; max-height: 300px; min-height: 300px; overflow-y: auto; padding: 5px; }上述
main.css檔案會設定應用程式的樣式。以下列 JSON 來建立
src/tsconfig.json:{ "compilerOptions": { "target": "es5" } }上述程式碼會設定 TypeScript 編譯器來產生 ECMAScript 5 相容的 JavaScript。
使用下列程式碼建立
src/index.ts:import "./css/main.css"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.keyCode === 13) { send(); } }); btnSend.addEventListener("click", send); function send() { }上述的 TypeScript 會擷取 DOM 項目的參考,並將附加兩個事件處理常式:
-
keyup:此事件會在使用者輸入tbMessage文字方塊時引發。 當使用者按下send鍵時,即會呼叫 函式。 -
click:此事件會在使用者選取 [傳送] 按鈕時引發。 系統會呼叫send函式。
-
設定 ASP.NET Core 應用程式
Startup.Configure方法中提供的程式碼會顯示 Hello World!。 將app.Run方法呼叫取代為對 UseDefaultFiles(IApplicationBuilder) 和 UseStaticFiles(IApplicationBuilder) 的呼叫。app.UseDefaultFiles(); app.UseStaticFiles();上述程式碼可讓伺服器找出並提供
index.html檔案,無論使用者輸入的是其完整 URL 還是 Web 應用程式的根 URL。在 AddSignalR 中呼叫
Startup.ConfigureServices。 這會將 SignalR 服務新增至專案。services.AddSignalR();將 /hub 路由對應至
ChatHub中樞。 在Startup.Configure的結尾新增以下幾行:app.UseSignalR(options => { options.MapHub<ChatHub>("/hub"); });在專案根目錄中建立名為 Hubs 的新目錄。 其目的是要儲存下一個步驟所建立的 SignalR 中樞。
使用下列程式碼建立中樞
Hubs/ChatHub.cs:using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRWebPack.Hubs { public class ChatHub : Hub { } }在
Startup.cs檔案的頂端新增下列程式碼,以解析ChatHub參考:using SignalRWebPack.Hubs;
啟用用戶端與伺服器的通訊
應用程式目前會顯示一個簡單的表單來傳送訊息。 當您嘗試這樣做時,不會執行任何動作。 伺服器正在接聽特定的路由,但對於已傳送的訊息不會進行任何處理。
在專案根目錄下執行下列命令:
npm install @aspnet/signalr上述命令會安裝 SignalR TypeScript 用戶端,可讓用戶端將訊息傳送至伺服器。
將醒目提示的程式碼新增至
src/index.ts檔案:import "./css/main.css"; import * as signalR from "@aspnet/signalr"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { let m = document.createElement("div"); m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(m); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch(err => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.keyCode === 13) { send(); } }); btnSend.addEventListener("click", send); function send() { }上述程式碼支援從伺服器接收訊息。
HubConnectionBuilder類別會建立用於設定伺服器連線的新產生器。withUrl函式則會設定中樞 URL。SignalR 可讓用戶端與伺服器之間進行訊息交換。 每個訊息都有特定的名稱。 例如,名稱為
messageReceived的訊息可以執行負責在訊息區域中顯示新訊息的邏輯。 接聽特定的訊息可透過on函式完成。 您可以接聽任意數目的訊息名稱。 也可將參數傳遞給訊息,例如作者的名稱和已接收訊息的內容。 用戶端收到訊息之後,就會在其div屬性中使用作者的名稱和訊息內容建立新的innerHTML元素。 新訊息會新增至顯示訊息的主要div元素。現在用戶端可以接收訊息,請設定它來傳送訊息。 將醒目提示的程式碼新增至
src/index.ts檔案:import "./css/main.css"; import * as signalR from "@aspnet/signalr"; const divMessages: HTMLDivElement = document.querySelector("#divMessages"); const tbMessage: HTMLInputElement = document.querySelector("#tbMessage"); const btnSend: HTMLButtonElement = document.querySelector("#btnSend"); const username = new Date().getTime(); const connection = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); connection.on("messageReceived", (username: string, message: string) => { let messageContainer = document.createElement("div"); messageContainer.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`; divMessages.appendChild(messageContainer); divMessages.scrollTop = divMessages.scrollHeight; }); connection.start().catch(err => document.write(err)); tbMessage.addEventListener("keyup", (e: KeyboardEvent) => { if (e.keyCode === 13) { send(); } }); btnSend.addEventListener("click", send); function send() { connection.send("newMessage", username, tbMessage.value) .then(() => tbMessage.value = ""); }透過 Websocket 連線傳送訊息需要呼叫
send方法。 方法的第一個參數是訊息名稱。 訊息資料則佔用其他參數。 在此範例中,識別為newMessage的訊息會傳送到伺服器。 訊息是由使用者名稱和文字方塊中的使用者輸入所組成。 如果傳送可正常運作,則會清除文字方塊的值。將
NewMessage方法新增至ChatHub類別:using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRWebPack.Hubs { public class ChatHub : Hub { public async Task NewMessage(long username, string message) { await Clients.All.SendAsync("messageReceived", username, message); } } }上述程式碼會在伺服器收到訊息之後,向所有連線的使用者廣播已接收的訊息。 不需要讓泛型
on方法接收所有訊息。 以訊息名稱命名方法就已足夠。在此範例中,TypeScript 用戶端會傳送一則識別為
newMessage的訊息。 C#NewMessage方法預期有用戶端所傳送的資料。 在 SendAsync 上呼叫 。 接收的訊息就會傳送到連線至中樞的所有用戶端。
測試應用程式
使用下列步驟確認應用程式可正常運作。
在 release 模式下執行 Webpack。 使用 [套件管理員主控台] 視窗,在專案根目錄中執行下列命令。 如果您不在專案根目錄中,請先輸入
cd SignalRWebPack,再輸入命令。npm run release此命令會產生執行應用程式時要提供的用戶端資產。 這些資產位於
wwwroot資料夾中。Webpack 已完成下列工作:
- 清除
wwwroot目錄的內容。 - 在名為轉譯的程序中將 TypeScript 轉換為 JavaScript。
- 在名為縮減的程序中,破壞產生的 JavaScript 以縮減檔案大小。
- 將處理的 JavaScript、CSS 與 HTML 檔案從
src複製到wwwroot目錄。 - 將下列元素插入
wwwroot/index.html檔案中:- 一個
<link>標籤,參考wwwroot/main.<hash>.css檔案。 此標記緊接在結尾</head>標記之前。 - 一個
<script>標籤,參考已縮減的wwwroot/main.<hash>.js檔案。 此標籤緊接在</title>結尾標籤後面。
- 一個
- 清除
選取 [偵錯]>[啟動但不偵錯],啟動瀏覽器中的應用程式,但不附加偵錯工具。
wwwroot/index.html檔案會提供於http://localhost:<port_number>。開啟另一個瀏覽器執行個體 (任何瀏覽器)。 在網址列中貼上 URL。
選擇其中一個瀏覽器,在 [訊息] 文字方塊中鍵入某些內容,然後選取 [傳送] 按鈕。 唯一使用者名稱和訊息會立即顯示在兩個頁面上。