チュートリアル: TypeScript と Webpack を使用した ASP.NET Core SignalR の概要

Note

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

作成者: Sébastien Sougnez

このチュートリアルでは、TypeScript で記述されたクライアントをバンドルおよびビルドするための、ASP.NET Core SignalR Web アプリでの Webpack の使用法を示します。 Webpack を使用すると、開発者は Web アプリのクライアント側のリソースをバンドルおよびビルドすることができます。

このチュートリアルでは、以下の内容を学習します。

  • ASP.NET Core SignalR アプリを作成する
  • SignalR サーバーを構成する
  • Webpack を使用してビルド パイプラインを構成する
  • SignalR TypeScript クライアントを構成する
  • クライアントとサーバー間の通信を有効にする

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

必須コンポーネント

ASP.NET Core Web アプリを作成する

既定では、Visual Studio は、そのインストール ディレクトリ内で見つかった npm のバージョンを使用します。 PATH 環境変数で npm を検索するように Visual Studio を構成するには:

Visual Studio を起動します。 スタート ウィンドウで、 [コードなしで続行] を選択します。

  1. [ツール]>[オプション]>[プロジェクトとソリューション]>[Web パッケージ管理]>[外部 Web ツール] に移動します。

  2. リストから $(PATH) エントリを選択します。 上向き矢印を選択して、エントリをリストの 2 番目の位置に移動し、[OK] を選択します。

    Visual Studio の構成

新しい ASP.NET Core Web アプリを作成するには:

  1. [ファイル]>[新規]>[プロジェクト] メニュー オプションを使用して、[ASP.NET Core 空] テンプレートを選択します。 [次へ] を選択します。
  2. プロジェクトに SignalRWebpack という名前を付け、[作成] を選択します。
  3. [フレームワーク] ドロップダウンから [.NET 8.0 (長期的なサポート)] を選択します。 [作成] を選択します

プロジェクトに Microsoft.TypeScript.MSBuild NuGet パッケージを追加します。

  1. ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[NuGet パッケージの管理] を選択します。 [参照] タブで Microsoft.TypeScript.MSBuild を検索し、右側にある [インストール] を選択してパッケージをインストールします。

Visual Studio によってソリューション エクスプローラー[依存関係] ノードの下に NuGet パッケージが追加され、プロジェクトで TypeScript のコンパイルができるようになります。

サーバーの構成

このセクションでは、SignalR メッセージを送受信するように ASP.NET Core Web アプリを構成します。

  1. Program.cs で、AddSignalR を呼び出します。

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddSignalR();
    
  2. 再度、Program.cs で、UseDefaultFilesUseStaticFiles を呼び出します。

    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    上記のコードを使用すると、サーバーで "index.html" ファイルを検索して提供できるようになります。 ファイルは、ユーザーが Web アプリの完全な URL またはルート URL を入力した場合に提供されます。

  3. SignalR ハブ クラス用に、プロジェクト ルート SignalRWebpack/Hubs という名前の新しいディレクトリを作成します。

  4. 次のコードを使用して新しいファイル 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 メソッドは、データがクライアントから送信されることを想定しています。
    • Clients.All 上の SendAsync に対して呼び出しが行われます。
    • 受信したメッセージは、ハブに接続されているすべてのクライアントに送信されます。
  5. ChatHub 参照を解決するには、次の using ステートメントを Program.cs の先頭に追加します。

    using SignalRWebpack.Hubs;
    
  6. Program.cs で、/hub ルートを ChatHub ハブにマップします。 Hello World! を表示するコードを次のコードに置き換えます。

    app.MapHub<ChatHub>("/hub");
    

クライアントの構成

このセクションでは、Node.js プロジェクトを作成して TypeScript を JavaScript に変換し、Webpack を使用してクライアント側リソース (HTML や CSS など) をバンドルします。

  1. プロジェクト ルートで次のコマンドを実行して、"package.json" ファイルを作成します。

    npm init -y
    
  2. 強調表示されているプロパティを "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 に設定して、次の手順でパッケージのインストールの警告が表示されないようにします。

  3. 必要な npm パッケージをインストールします。 プロジェクト ルートで、次のコマンドを実行します。

    npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
    

    -E オプションは、セマンティック バージョン管理範囲演算子を package.json に書き込む npm の既定の動作を無効にします。 たとえば、"webpack": "5.76.1""webpack": "^5.76.1" の代わりに使用されています。 このオプションにより、新しいパッケージ バージョンへの予期しないアップグレードが防止されます。

    詳細については、npm-install のドキュメントを参照してください。

  4. package.json ファイルの scripts プロパティを次のコードで置き換えます。

    "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 コマンドを呼び出してアプリを公開します。
  5. プロジェクト ルートに、次のコードを含む "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 配列には、SignalR クライアント JavaScript をインポートするための .js が含まれています。
  6. プロジェクト ルート SignalRWebpack/ にクライアント コード用の src という名前の新しいディレクトリを作成します。

  7. サンプル プロジェクトからプロジェクト ルートに 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: ECMAScript 5 対応の JavaScript を生成するように、TypeScript コンパイラを構成します。

      {
        "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 要素への参照を取得し、次の 2 つのイベント ハンドラーをアタッチします。

      • keyup: ユーザーが tbMessage テキスト ボックス内に入力したときに発生し、ユーザーが Enter キーを押したときに send 関数を呼び出します。
      • click: ユーザーが [送信] ボタンを選択したときに発生し、send 関数を呼び出します。

      HubConnectionBuilder クラスは、サーバー接続を構成するための新しいビルダーを作成します。 withUrl 関数は、ハブ URL を構成します。

      SignalR により、クライアントとサーバー間でのメッセージのやり取りが可能になります。 各メッセージには特定の名前があります。 たとえば、messageReceived という名前のメッセージは、メッセージ ゾーンに新しいメッセージを表示するためのロジックを実行できます。 特定のメッセージをリッスンするには、on 関数を使用します。 任意の数のメッセージ名をリッスンできます。 作成者の名前や受信したメッセージの内容など、パラメーターをメッセージに渡すこともできます。 クライアントがメッセージを受信すると、innerHTML 属性に作成者の名前とメッセージ コンテンツを持つ新しい div 要素が作成されます。 これはメッセージを表示する主要な div 要素に追加されます。

      WebSocket 接続を介してメッセージを送信するには、send メソッドを呼び出す必要があります。 メソッドの最初のパラメーターは、メッセージ名です。 メッセージ データは、他のパラメーターに存在しています。 この例では、newMessage として識別されたメッセージがサーバーに送信されます。 メッセージは、ユーザー名と、テキスト ボックスへのユーザー入力で構成されます。 送信が機能していると、テキスト ボックスの値はクリアされます。

  8. プロジェクト ルートで、次のコマンドを実行します。

    npm i @microsoft/signalr @types/node
    

    上記のコマンドにより、次がインストールされます。

    • SignalR TypeScript クライアント。クライアントがサーバーにメッセージを送信できるようになります。
    • Node.js の TypeScript 型定義。Node.js 型のコンパイル時チェックが有効になります。

アプリをテストする

次の手順に従ってアプリの動作を確認します。

  1. release モードで Webpack を実行します。 パッケージ マネージャー コンソール ウィンドウを使用して、プロジェクト ルートで次のコマンドを実行します。

    npm run release
    

    このコマンドは、アプリの実行中に提供するクライアント側資産を生成します。 資産は、wwwroot フォルダーに配置されます。

    Webpack は、次のタスクを完了しました。

    • wwwroot ディレクトリの内容を消去しました。
    • "トランスコンパイル" と呼ばれるプロセスで TypeScript を JavaScript に変換しました。
    • "縮小" と呼ばれるプロセスで、生成後の JavaScript ファイルのサイズを縮小しました。
    • 処理済みの JavaScript、CSS、および HTML ファイルを src から wwwroot ディレクトリにコピーしました。
    • 次の要素を wwwroot/index.html ファイルに挿入しました。
      • wwwroot/main.<hash>.css ファイルを参照している <link> タグ。 このタグは、終了 </head> タグの直前に置かれます。
      • 縮小された wwwroot/main.<hash>.js ファイルを参照している <script> タグ。 このタグは、終了 </title> タグの直後に置かれます。
  2. [デバッグ]>[デバッグなしで開始] の順に選択して、デバッガーをアタッチせずに、ブラウザーでアプリを起動します。 wwwroot/index.html ファイルは https://localhost:<port> で提供されます。

    コンパイル エラーが発生した場合は、ソリューションを閉じてから再度開いてみてください。

  3. 別のブラウザー インスタンスを開き (どのブラウザーでもかまいません)、アドレス バーに URL を貼り付けます。

  4. いずれかのブラウザーを選択し、[メッセージ] テキスト ボックスにメッセージを入力し、[送信] ボタンを選択します。 次の瞬間、両方のページに一意のユーザー名とメッセージが表示されます。

両方のブラウザー ウィンドウに表示されるメッセージ

次の手順

その他のリソース

このチュートリアルでは、TypeScript で記述されたクライアントをバンドルおよびビルドするための、ASP.NET Core SignalR Web アプリでの Webpack の使用法を示します。 Webpack を使用すると、開発者は Web アプリのクライアント側のリソースをバンドルおよびビルドすることができます。

このチュートリアルでは、以下の内容を学習します。

  • ASP.NET Core SignalR アプリを作成する
  • SignalR サーバーを構成する
  • Webpack を使用してビルド パイプラインを構成する
  • SignalR TypeScript クライアントを構成する
  • クライアントとサーバー間の通信を有効にする

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

必須コンポーネント

ASP.NET Core Web アプリを作成する

既定では、Visual Studio は、そのインストール ディレクトリ内で見つかった npm のバージョンを使用します。 PATH 環境変数で npm を検索するように Visual Studio を構成するには:

Visual Studio を起動します。 スタート ウィンドウで、 [コードなしで続行] を選択します。

  1. [ツール]>[オプション]>[プロジェクトとソリューション]>[Web パッケージ管理]>[外部 Web ツール] に移動します。

  2. リストから $(PATH) エントリを選択します。 上向き矢印を選択して、エントリをリストの 2 番目の位置に移動し、[OK] を選択します。

    Visual Studio の構成

新しい ASP.NET Core Web アプリを作成するには:

  1. [ファイル]>[新規]>[プロジェクト] メニュー オプションを使用して、[ASP.NET Core 空] テンプレートを選択します。 [次へ] を選択します。
  2. プロジェクトに SignalRWebpack という名前を付け、[作成] を選択します。
  3. [フレームワーク] ドロップダウンから .NET 7.0 (Standard Term Support) を選択します。 [作成] を選択します

プロジェクトに Microsoft.TypeScript.MSBuild NuGet パッケージを追加します。

  1. ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[NuGet パッケージの管理] を選択します。 [参照] タブで Microsoft.TypeScript.MSBuild を検索し、右側にある [インストール] を選択してパッケージをインストールします。

Visual Studio によってソリューション エクスプローラー[依存関係] ノードの下に NuGet パッケージが追加され、プロジェクトで TypeScript のコンパイルができるようになります。

サーバーの構成

このセクションでは、SignalR メッセージを送受信するように ASP.NET Core Web アプリを構成します。

  1. Program.cs で、AddSignalR を呼び出します。

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddSignalR();
    
  2. 再度、Program.cs で、UseDefaultFilesUseStaticFiles を呼び出します。

    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    上記のコードを使用すると、サーバーで "index.html" ファイルを検索して提供できるようになります。 ファイルは、ユーザーが Web アプリの完全な URL またはルート URL を入力した場合に提供されます。

  3. SignalR ハブ クラス用に、プロジェクト ルート SignalRWebpack/Hubs という名前の新しいディレクトリを作成します。

  4. 次のコードを使用して新しいファイル 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 メソッドは、データがクライアントから送信されることを想定しています。
    • Clients.All 上の SendAsync に対して呼び出しが行われます。
    • 受信したメッセージは、ハブに接続されているすべてのクライアントに送信されます。
  5. ChatHub 参照を解決するには、次の using ステートメントを Program.cs の先頭に追加します。

    using SignalRWebpack.Hubs;
    
  6. Program.cs で、/hub ルートを ChatHub ハブにマップします。 Hello World! を表示するコードを次のコードに置き換えます。

    app.MapHub<ChatHub>("/hub");
    

クライアントの構成

このセクションでは、Node.js プロジェクトを作成して TypeScript を JavaScript に変換し、Webpack を使用してクライアント側リソース (HTML や CSS など) をバンドルします。

  1. プロジェクト ルートで次のコマンドを実行して、"package.json" ファイルを作成します。

    npm init -y
    
  2. 強調表示されているプロパティを "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 に設定して、次の手順でパッケージのインストールの警告が表示されないようにします。

  3. 必要な npm パッケージをインストールします。 プロジェクト ルートで、次のコマンドを実行します。

    npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
    

    -E オプションは、セマンティック バージョン管理範囲演算子を package.json に書き込む npm の既定の動作を無効にします。 たとえば、"webpack": "5.76.1""webpack": "^5.76.1" の代わりに使用されています。 このオプションにより、新しいパッケージ バージョンへの予期しないアップグレードが防止されます。

    詳細については、npm-install のドキュメントを参照してください。

  4. package.json ファイルの scripts プロパティを次のコードで置き換えます。

    "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 コマンドを呼び出してアプリを公開します。
  5. プロジェクト ルートに、次のコードを含む "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 配列には、SignalR クライアント JavaScript をインポートするための .js が含まれています。
  6. サンプル プロジェクトからプロジェクト ルートに 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: ECMAScript 5 対応の JavaScript を生成するように、TypeScript コンパイラを構成します。

      {
        "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 要素への参照を取得し、次の 2 つのイベント ハンドラーをアタッチします。

      • keyup: ユーザーが tbMessage テキスト ボックス内に入力したときに発生し、ユーザーが Enter キーを押したときに send 関数を呼び出します。
      • click: ユーザーが [送信] ボタンを選択したときに発生し、send 関数を呼び出します。

      HubConnectionBuilder クラスは、サーバー接続を構成するための新しいビルダーを作成します。 withUrl 関数は、ハブ URL を構成します。

      SignalR により、クライアントとサーバー間でのメッセージのやり取りが可能になります。 各メッセージには特定の名前があります。 たとえば、messageReceived という名前のメッセージは、メッセージ ゾーンに新しいメッセージを表示するためのロジックを実行できます。 特定のメッセージをリッスンするには、on 関数を使用します。 任意の数のメッセージ名をリッスンできます。 作成者の名前や受信したメッセージの内容など、パラメーターをメッセージに渡すこともできます。 クライアントがメッセージを受信すると、innerHTML 属性に作成者の名前とメッセージ コンテンツを持つ新しい div 要素が作成されます。 これはメッセージを表示する主要な div 要素に追加されます。

      WebSocket 接続を介してメッセージを送信するには、send メソッドを呼び出す必要があります。 メソッドの最初のパラメーターは、メッセージ名です。 メッセージ データは、他のパラメーターに存在しています。 この例では、newMessage として識別されたメッセージがサーバーに送信されます。 メッセージは、ユーザー名と、テキスト ボックスへのユーザー入力で構成されます。 送信が機能していると、テキスト ボックスの値はクリアされます。

  7. プロジェクト ルートで、次のコマンドを実行します。

    npm i @microsoft/signalr @types/node
    

    上記のコマンドにより、次がインストールされます。

    • SignalR TypeScript クライアント。クライアントがサーバーにメッセージを送信できるようになります。
    • Node.js の TypeScript 型定義。Node.js 型のコンパイル時チェックが有効になります。

アプリをテストする

次の手順に従ってアプリの動作を確認します。

  1. release モードで Webpack を実行します。 パッケージ マネージャー コンソール ウィンドウを使用して、プロジェクト ルートで次のコマンドを実行します。

    npm run release
    

    このコマンドは、アプリの実行中に提供するクライアント側資産を生成します。 資産は、wwwroot フォルダーに配置されます。

    Webpack は、次のタスクを完了しました。

    • wwwroot ディレクトリの内容を消去しました。
    • "トランスコンパイル" と呼ばれるプロセスで TypeScript を JavaScript に変換しました。
    • "縮小" と呼ばれるプロセスで、生成後の JavaScript ファイルのサイズを縮小しました。
    • 処理済みの JavaScript、CSS、および HTML ファイルを src から wwwroot ディレクトリにコピーしました。
    • 次の要素を wwwroot/index.html ファイルに挿入しました。
      • wwwroot/main.<hash>.css ファイルを参照している <link> タグ。 このタグは、終了 </head> タグの直前に置かれます。
      • 縮小された wwwroot/main.<hash>.js ファイルを参照している <script> タグ。 このタグは、終了 </title> タグの直後に置かれます。
  2. [デバッグ]>[デバッグなしで開始] の順に選択して、デバッガーをアタッチせずに、ブラウザーでアプリを起動します。 wwwroot/index.html ファイルは https://localhost:<port> で提供されます。

    コンパイル エラーが発生した場合は、ソリューションを閉じてから再度開いてみてください。

  3. 別のブラウザー インスタンスを開き (どのブラウザーでもかまいません)、アドレス バーに URL を貼り付けます。

  4. いずれかのブラウザーを選択し、[メッセージ] テキスト ボックスにメッセージを入力し、[送信] ボタンを選択します。 次の瞬間、両方のページに一意のユーザー名とメッセージが表示されます。

両方のブラウザー ウィンドウに表示されるメッセージ

次の手順

その他のリソース

このチュートリアルでは、TypeScript で記述されたクライアントをバンドルおよびビルドするための、ASP.NET Core SignalR Web アプリでの Webpack の使用法を示します。 Webpack を使用すると、開発者は Web アプリのクライアント側のリソースをバンドルおよびビルドすることができます。

このチュートリアルでは、以下の内容を学習します。

  • ASP.NET Core SignalR アプリを作成する
  • SignalR サーバーを構成する
  • Webpack を使用してビルド パイプラインを構成する
  • SignalR TypeScript クライアントを構成する
  • クライアントとサーバー間の通信を有効にする

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

必須コンポーネント

ASP.NET Core Web アプリを作成する

既定では、Visual Studio は、そのインストール ディレクトリ内で見つかった npm のバージョンを使用します。 PATH 環境変数で npm を検索するように Visual Studio を構成するには:

  1. Visual Studio を起動します。 スタート ウィンドウで、 [コードなしで続行] を選択します。

  2. [ツール]>[オプション]>[プロジェクトとソリューション]>[Web パッケージ管理]>[外部 Web ツール] に移動します。

  3. リストから $(PATH) エントリを選択します。 上向き矢印を選択して、エントリをリストの 2 番目の位置に移動し、[OK] を選択します。

    Visual Studio の構成

新しい ASP.NET Core Web アプリを作成するには:

  1. [ファイル]>[新規]>[プロジェクト] メニュー オプションを使用して、[ASP.NET Core 空] テンプレートを選択します。 [次へ] を選択します。
  2. プロジェクトに SignalRWebpack という名前を付け、[作成] を選択します。
  3. [フレームワーク] ドロップダウンから .NET 6.0 (Long Term Support) を選択します。 [作成] を選択します

プロジェクトに Microsoft.TypeScript.MSBuild NuGet パッケージを追加します。

  1. ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[NuGet パッケージの管理] を選択します。 [参照] タブで Microsoft.TypeScript.MSBuild を検索し、右側にある [インストール] を選択してパッケージをインストールします。

Visual Studio によってソリューション エクスプローラー[依存関係] ノードの下に NuGet パッケージが追加され、プロジェクトで TypeScript のコンパイルができるようになります。

サーバーの構成

このセクションでは、SignalR メッセージを送受信するように ASP.NET Core Web アプリを構成します。

  1. Program.cs で、AddSignalR を呼び出します。

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddSignalR();
    
  2. 再度、Program.cs で、UseDefaultFilesUseStaticFiles を呼び出します。

    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    上記のコードを使用すると、サーバーで "index.html" ファイルを検索して提供できるようになります。 ファイルは、ユーザーが Web アプリの完全な URL またはルート URL を入力した場合に提供されます。

  3. SignalR ハブ クラス用に、プロジェクト ルート SignalRWebpack/Hubs という名前の新しいディレクトリを作成します。

  4. 次のコードを使用して新しいファイル 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 メソッドは、データがクライアントから送信されることを想定しています。 Clients.All 上の SendAsync に対して呼び出しが行われます。 受信したメッセージは、ハブに接続されているすべてのクライアントに送信されます。

  5. ChatHub 参照を解決するには、次の using ステートメントを Program.cs の先頭に追加します。

    using SignalRWebpack.Hubs;
    
  6. Program.cs で、/hub ルートを ChatHub ハブにマップします。 Hello World! を表示するコードを次のコードに置き換えます。

    app.MapHub<ChatHub>("/hub");
    

クライアントの構成

このセクションでは、Node.js プロジェクトを作成して TypeScript を JavaScript に変換し、Webpack を使用してクライアント側リソース (HTML や CSS など) をバンドルします。

  1. プロジェクト ルートで次のコマンドを実行して、"package.json" ファイルを作成します。

    npm init -y
    
  2. 強調表示されているプロパティを "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 に設定して、次の手順でパッケージのインストールの警告が表示されないようにします。

  3. 必要な npm パッケージをインストールします。 プロジェクト ルートで、次のコマンドを実行します。

    npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
    

    -E オプションは、セマンティック バージョン管理範囲演算子を package.json に書き込む npm の既定の動作を無効にします。 たとえば、"webpack": "5.70.0""webpack": "^5.70.0" の代わりに使用されています。 このオプションにより、新しいパッケージ バージョンへの予期しないアップグレードが防止されます。

    詳細については、npm-install のドキュメントを参照してください。

  4. package.json ファイルの scripts プロパティを次のコードで置き換えます。

    "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 コマンドを呼び出してアプリを公開します。
  5. プロジェクト ルートに、次のコードを含む "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 配列には、SignalR クライアント JavaScript をインポートするための .js が含まれています。
  6. サンプル プロジェクトからプロジェクト ルートに 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: ECMAScript 5 対応の JavaScript を生成するように、TypeScript コンパイラを構成します。

      {
        "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 要素への参照を取得し、次の 2 つのイベント ハンドラーをアタッチします。

    • keyup: ユーザーが tbMessage テキスト ボックス内に入力したときに発生し、ユーザーが Enter キーを押したときに send 関数を呼び出します。
    • click: ユーザーが [送信] ボタンを選択したときに発生し、send 関数を呼び出します。

    HubConnectionBuilder クラスは、サーバー接続を構成するための新しいビルダーを作成します。 withUrl 関数は、ハブ URL を構成します。

    SignalR により、クライアントとサーバー間でのメッセージのやり取りが可能になります。 各メッセージには特定の名前があります。 たとえば、messageReceived という名前のメッセージは、メッセージ ゾーンに新しいメッセージを表示するためのロジックを実行できます。 特定のメッセージをリッスンするには、on 関数を使用します。 任意の数のメッセージ名をリッスンできます。 作成者の名前や受信したメッセージの内容など、パラメーターをメッセージに渡すこともできます。 クライアントがメッセージを受信すると、innerHTML 属性に作成者の名前とメッセージ コンテンツを持つ新しい div 要素が作成されます。 これはメッセージを表示する主要な div 要素に追加されます。

    WebSocket 接続を介してメッセージを送信するには、send メソッドを呼び出す必要があります。 メソッドの最初のパラメーターは、メッセージ名です。 メッセージ データは、他のパラメーターに存在しています。 この例では、newMessage として識別されたメッセージがサーバーに送信されます。 メッセージは、ユーザー名と、テキスト ボックスへのユーザー入力で構成されます。 送信が機能していると、テキスト ボックスの値はクリアされます。

  7. プロジェクト ルートで、次のコマンドを実行します。

    npm i @microsoft/signalr @types/node
    

    上記のコマンドにより、次がインストールされます。

    • SignalR TypeScript クライアント。クライアントがサーバーにメッセージを送信できるようになります。
    • Node.js の TypeScript 型定義。Node.js 型のコンパイル時チェックが有効になります。

アプリをテストする

次の手順に従ってアプリの動作を確認します。

  1. release モードで Webpack を実行します。 パッケージ マネージャー コンソール ウィンドウを使用して、プロジェクト ルートで次のコマンドを実行します。 プロジェクト ルートになっていない場合は、コマンドを入力する前に「cd SignalRWebpack」と入力します。

    npm run release
    

    このコマンドは、アプリの実行中に提供するクライアント側資産を生成します。 資産は、wwwroot フォルダーに配置されます。

    Webpack は、次のタスクを完了しました。

    • wwwroot ディレクトリの内容を消去しました。
    • "トランスコンパイル" と呼ばれるプロセスで TypeScript を JavaScript に変換しました。
    • "縮小" と呼ばれるプロセスで、生成後の JavaScript ファイルのサイズを縮小しました。
    • 処理済みの JavaScript、CSS、および HTML ファイルを src から wwwroot ディレクトリにコピーしました。
    • 次の要素を wwwroot/index.html ファイルに挿入しました。
      • wwwroot/main.<hash>.css ファイルを参照している <link> タグ。 このタグは、終了 </head> タグの直前に置かれます。
      • 縮小された wwwroot/main.<hash>.js ファイルを参照している <script> タグ。 このタグは、終了 </title> タグの直後に置かれます。
  2. [デバッグ]>[デバッグなしで開始] の順に選択して、デバッガーをアタッチせずに、ブラウザーでアプリを起動します。 wwwroot/index.html ファイルは https://localhost:<port> で提供されます。

    コンパイル エラーが発生した場合は、ソリューションを閉じてから再度開いてみてください。

  3. 別のブラウザー インスタンスを開き (どのブラウザーでもかまいません)、アドレス バーに URL を貼り付けます。

  4. いずれかのブラウザーを選択し、[メッセージ] テキスト ボックスにメッセージを入力し、[送信] ボタンを選択します。 次の瞬間、両方のページに一意のユーザー名とメッセージが表示されます。

両方のブラウザー ウィンドウに表示されるメッセージ

次の手順

その他のリソース

このチュートリアルでは、TypeScript で記述されたクライアントをバンドルおよびビルドするための、ASP.NET Core SignalR Web アプリでの Webpack の使用法を示します。 Webpack を使用すると、開発者は Web アプリのクライアント側のリソースをバンドルおよびビルドすることができます。

このチュートリアルでは、次の作業を行う方法について説明します。

  • スターター ASP.NET Core SignalR アプリをスキャフォールディングする
  • SignalR TypeScript クライアントを構成する
  • Webpack を使用してビルド パイプラインを構成する
  • SignalR サーバーを構成する
  • クライアントとサーバー間の通信を有効にする

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

必須コンポーネント

ASP.NET Core Web アプリを作成する

PATH 環境変数で npm を検索するように Visual Studio を構成します。 既定では、Visual Studio は、そのインストール ディレクトリ内で見つかった npm のバージョンを使用します。 Visual Studio のこれらの説明に従ってください。

  1. Visual Studio を起動します。 スタート ウィンドウで、 [コードなしで続行] を選択します。

  2. [ツール]>[オプション]>[プロジェクトとソリューション]>[Web パッケージ管理]>[外部 Web ツール] に移動します。

  3. リストから [$(PATH)] エントリを選択します。 上向き矢印を選択して、エントリをリストの 2 番目の位置に移動し、[OK] を選択します。

    Visual Studio の構成

Visual Studio の構成が完了しました。

  1. [ファイル]>[新規]>[プロジェクト] メニュー オプションを使用して、 [ASP.NET Core Web アプリケーション] テンプレートを選択します。 [次へ] を選択します。
  2. プロジェクトに *SignalRWebPac`` という名前を付け、[作成] を選択します。
  3. ターゲット フレームワークのドロップダウンから [.NET Core] を選択し、フレームワーク セレクターのドロップダウンから [ASP.NET Core 3.1] を選択します。 のテンプレートを選択して、 [作成] を選択します。

Microsoft.TypeScript.MSBuild パッケージをプロジェクトに追加します。

  1. ソリューション エクスプローラー (右ペイン) でプロジェクト ノードを右クリックし、 [NuGet パッケージの管理] を選択します。 [参照] タブで Microsoft.TypeScript.MSBuild を検索し、右側にある [インストール] をクリックしてパッケージをインストールします。

Visual Studio によってソリューション エクスプローラー[依存関係] ノードの下に NuGet パッケージが追加され、プロジェクトで TypeScript のコンパイルができるようになります。

WebPack および TypeScript の構成

次の手順では、TypeScript の JavaScript への変換と、クライアント側のリソースのバンドルを構成します。

  1. プロジェクト ルートで次のコマンドを実行して、"package.json" ファイルを作成します。

    npm init -y
    
  2. 強調表示されているプロパティを "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 に設定して、次の手順でパッケージのインストールの警告が表示されないようにします。

  3. 必要な 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 オプションは、セマンティック バージョニング範囲演算子を *packagejsonに書き込む npm の既定の動作を無効にします。 たとえば、"webpack": "4.41.5""webpack": "^4.41.5" の代わりに使用されています。 このオプションにより、新しいパッケージ バージョンへの予期しないアップグレードが防止されます。

    詳細については、npm-install のドキュメントを参照してください。

  4. "package.json" ファイルの scripts プロパティを次のコードで置き換えます。

    "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 Core CLI の publish コマンドを呼び出してアプリを公開します。
  5. プロジェクト ルートに、次のコードを含む "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 配列には、SignalR クライアント JavaScript をインポートするための .js が含まれています。
  6. プロジェクトのクライアント側アセットを格納するために、プロジェクト ルートに新しい "src" ディレクトリを作成します。

  7. 次のマークアップを含む "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 では、ホームページの定型マークアップが定義されます。

  8. 新しい src/css ディレクトリを作成します。 その目的は、プロジェクトの .css ファイルを格納することです。

  9. 次の 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 ファイルは、アプリをスタイル設定します。

  10. 次の JSON を含む src/tsconfig.json を作成します。

    {
      "compilerOptions": {
        "target": "es5"
      }
    }
    

    上記のコードは、ECMAScript 5 対応の JavaScript を生成するように TypeScript コンパイラを構成します。

  11. 次のコードを使用して 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 要素への参照を取得し、次の 2 つのイベント ハンドラーをアタッチします。

    • keyup:ユーザーが tbMessage テキスト ボックスに入力すると、このイベントが発生します。 ユーザーが Enter キーを押すと、send 関数が呼び出されます。
    • click: ユーザーが [送信] ボタンを選択すると、このイベントが発生します。 send 関数が呼び出されます。

Configure the app

  1. 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" ファイルを検索して提供できるようになります。 ファイルは、ユーザーが Web アプリの完全な URL またはルート URL を入力した場合に提供されます。

  2. Startup.Configure の最後で、" /hub" ルートを ChatHub ハブにマップします。 Hello World! を表示するコードを次の行に置き換えます。

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/hub");
    });
    
  3. Startup.ConfigureServices で、AddSignalR を呼び出します。

    services.AddSignalR();
    
  4. SignalR ハブを格納するために、プロジェクト ルート SignalRWebPack/Hubs という名前の新しいディレクトリを作成します。

  5. 次のコードを使用して、Hubs/ChatHub.cs を作成します。

    using Microsoft.AspNetCore.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRWebPack.Hubs
    {
        public class ChatHub : Hub
        {
        }
    }
    
  6. ChatHub 参照を解決するには、次の using ステートメントを "Startup.cs" ファイルの先頭に追加します。

    using SignalRWebPack.Hubs;
    

クライアントとサーバーの通信を有効にする

現在、アプリにはメッセージを送信するための基本フォームが表示されていますが、まだ機能していません。 サーバーは特定のルートをリッスンしていますが、メッセージの送信については何も行いません。

  1. プロジェクト ルートで、次のコマンドを実行します。

    npm i @microsoft/signalr @types/node
    

    上記のコマンドにより、次がインストールされます。

    • SignalR TypeScript クライアント。クライアントがサーバーにメッセージを送信できるようになります。
    • Node.js の TypeScript 型定義。Node.js 型のコンパイル時チェックが有効になります。
  2. 強調表示されているコードを 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 関数を使用します。 任意の数のメッセージ名をリッスンできます。 作成者の名前や受信したメッセージの内容など、パラメーターをメッセージに渡すこともできます。 クライアントがメッセージを受信すると、innerHTML 属性に作成者の名前とメッセージ コンテンツを持つ新しい div 要素が作成されます。 これはメッセージを表示する主要な div 要素に追加されます。

  3. これでクライアントがメッセージを受信できるようになったので、メッセージを送信するように構成します。 強調表示されているコードを 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 として識別されたメッセージがサーバーに送信されます。 メッセージは、ユーザー名と、テキスト ボックスへのユーザー入力で構成されます。 送信が機能していると、テキスト ボックスの値はクリアされます。

  4. 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 メソッドは、データがクライアントから送信されることを想定しています。 Clients.All 上の SendAsync に対して呼び出しが行われます。 受信したメッセージは、ハブに接続されているすべてのクライアントに送信されます。

アプリのテスト

次の手順で、アプリの動作を確認します。

  1. リリース モードで Webpack を実行します。 パッケージ マネージャー コンソール ウィンドウを使用して、プロジェクト ルートで次のコマンドを実行します。 プロジェクト ルートになっていない場合は、コマンドを入力する前に「cd SignalRWebPack」と入力します。

    npm run release
    

    このコマンドは、アプリの実行中に提供するクライアント側資産を生成します。 資産は、wwwroot フォルダーに配置されます。

    Webpack は、次のタスクを完了しました。

    • wwwroot ディレクトリの内容を消去しました。
    • "トランスコンパイル" と呼ばれるプロセスで TypeScript を JavaScript に変換しました。
    • "縮小" と呼ばれるプロセスで、生成後の JavaScript ファイルのサイズを縮小しました。
    • 処理済みの JavaScript、CSS、および HTML ファイルを src から wwwroot ディレクトリにコピーしました。
    • 次の要素を wwwroot/index.html ファイルに挿入しました。
      • wwwroot/main.<hash>.css ファイルを参照している <link> タグ。 このタグは、終了 </head> タグの直前に置かれます。
      • 縮小された wwwroot/main.<hash>.js ファイルを参照している <script> タグ。 このタグは、終了 </title> タグの直後に置かれます。
  2. [デバッグ]>[デバッグなしで開始] の順に選択して、デバッガーをアタッチせずに、ブラウザーでアプリを起動します。 wwwroot/index.html ファイルは http://localhost:<port_number> で提供されます。

    コンパイル エラーが発生した場合は、ソリューションを閉じてから再度開いてみてください。

  3. 別のブラウザー インスタンスを開きます (任意のブラウザー)。 アドレス バーに URL を貼り付けます。

  4. いずれかのブラウザーを選択し、[メッセージ] テキスト ボックスにメッセージを入力し、[送信] ボタンを選択します。 次の瞬間、両方のページに一意のユーザー名とメッセージが表示されます。

両方のブラウザー ウィンドウに表示されるメッセージ

その他のリソース

このチュートリアルでは、TypeScript で記述されたクライアントをバンドルおよびビルドするための、ASP.NET Core SignalR Web アプリでの Webpack の使用法を示します。 Webpack を使用すると、開発者は Web アプリのクライアント側のリソースをバンドルおよびビルドすることができます。

このチュートリアルでは、次の作業を行う方法について説明します。

  • スターター ASP.NET Core SignalR アプリをスキャフォールディングする
  • SignalR TypeScript クライアントを構成する
  • Webpack を使用してビルド パイプラインを構成する
  • SignalR サーバーを構成する
  • クライアントとサーバー間の通信を有効にする

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

必須コンポーネント

ASP.NET Core Web アプリを作成する

PATH 環境変数で npm を検索するように Visual Studio を構成します。 既定では、Visual Studio は、そのインストール ディレクトリ内で見つかった npm のバージョンを使用します。 Visual Studio のこれらの説明に従ってください。

  1. [ツール]>[オプション]>[プロジェクトとソリューション]>[Web パッケージ管理]>[外部 Web ツール] に移動します。

  2. リストから [$(PATH)] エントリを選択します。 上向き矢印を選択して、エントリをリストの 2 番目の位置に移動します。

    Visual Studio の構成

Visual Studio の構成が完了しました。 次はプロジェクトを作成します。

  1. [ファイル]>[新規]>[プロジェクト] メニュー オプションを使用して、 [ASP.NET Core Web アプリケーション] テンプレートを選択します。
  2. プロジェクトに *SignalRWebPack` という名前を付け、[作成] を選択します。
  3. ターゲット フレームワークのドロップダウンから、 [.NET Core] を選択し、フレームワーク セレクターのドロップダウンから [ASP.NET Core 2.2] を選択します。 のテンプレートを選択して、 [作成] を選択します。

WebPack および TypeScript の構成

次の手順では、TypeScript の JavaScript への変換と、クライアント側のリソースのバンドルを構成します。

  1. プロジェクト ルートで次のコマンドを実行して、"package.json" ファイルを作成します。

    npm init -y
    
  2. 強調表示されているプロパティを 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 に設定して、次の手順でパッケージのインストールの警告が表示されないようにします。

  3. 必要な 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 オプションは、セマンティック バージョニング範囲演算子を *packagejsonに書き込む npm の既定の動作を無効にします。 たとえば、"webpack": "4.29.3""webpack": "^4.29.3" の代わりに使用されています。 このオプションにより、新しいパッケージ バージョンへの予期しないアップグレードが防止されます。

    詳細については、npm-install のドキュメントを参照してください。

  4. "package.json" ファイルの scripts プロパティを次のコードで置き換えます。

    "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 Core CLI の publish コマンドを呼び出してアプリを公開します。
  5. プロジェクト ルートに、次のコードを含む *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 配列には、SignalR クライアント JavaScript をインポートするための .js が含まれています。
  6. プロジェクトのクライアント側アセットを格納するために、プロジェクト ルートに新しい "src" ディレクトリを作成します。

  7. 次のマークアップを含む "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 では、ホームページの定型マークアップが定義されます。

  8. 新しい src/css ディレクトリを作成します。 その目的は、プロジェクトの .css ファイルを格納することです。

  9. 次のマークアップを含む 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 ファイルは、アプリをスタイル設定します。

  10. 次の JSON を含む src/tsconfig.json を作成します。

    {
      "compilerOptions": {
        "target": "es5"
      }
    }
    

    上記のコードは、ECMAScript 5 対応の JavaScript を生成するように TypeScript コンパイラを構成します。

  11. 次のコードを使用して 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 要素への参照を取得し、次の 2 つのイベント ハンドラーをアタッチします。

    • keyup:ユーザーが tbMessage テキスト ボックスに入力すると、このイベントが発生します。 ユーザーが Enter キーを押すと、send 関数が呼び出されます。
    • click: ユーザーが [送信] ボタンを選択すると、このイベントが発生します。 send 関数が呼び出されます。

ASP.NET Core アプリを構成する

  1. Startup.Configure メソッドで提供されたコードにより、Hello World! が表示されます。 app.Run のメソッド呼び出しを UseDefaultFiles(IApplicationBuilder)UseStaticFiles(IApplicationBuilder) の呼び出しに置き換えます。

    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    上記のコードにより、サーバーが index.html ファイルを見つけて提供することができます。ユーザーがファイルの完全な URL または Web アプリのルート URL を入力するかどうかは関係ありません。

  2. Startup.ConfigureServices 内で AddSignalR を呼び出します。 これにより SignalR サービスがプロジェクトに追加されます。

    services.AddSignalR();
    
  3. /hub ルートを ChatHub ハブにマップします。 Startup.Configure の末尾に次の行を追加します。

    app.UseSignalR(options =>
    {
        options.MapHub<ChatHub>("/hub");
    });
    
  4. プロジェクト ルートに Hubs という新しいディレクトリを作成します。 その目的は、次の手順で作成される SignalR ハブを格納することです。

  5. 次のコードを使用して、Hubs/ChatHub.cs を作成します。

    using Microsoft.AspNetCore.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRWebPack.Hubs
    {
        public class ChatHub : Hub
        {
        }
    }
    
  6. ChatHub 参照を解決するには、次のコードを Startup.cs ファイルの先頭に追加します。

    using SignalRWebPack.Hubs;
    

クライアントとサーバーの通信を有効にする

アプリには現在、メッセージを送信するための単純なフォームが表示されています。 送信しようとしても、何も起こりません。 サーバーは特定のルートをリッスンしていますが、メッセージの送信については何も行いません。

  1. プロジェクト ルートで、次のコマンドを実行します。

    npm install @aspnet/signalr
    

    上記のコマンドにより SignalR TypeScript クライアント がインストールされ、クライアントがサーバーにメッセージを送信できるようになります。

  2. 強調表示されているコードを 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 関数を使用します。 任意の数のメッセージ名をリッスンできます。 作成者の名前や受信したメッセージの内容など、パラメーターをメッセージに渡すこともできます。 クライアントがメッセージを受信すると、innerHTML 属性に作成者の名前とメッセージ コンテンツを持つ新しい div 要素が作成されます。 新しいメッセージが、メッセージを表示する主要な div 要素に追加されます。

  3. これでクライアントがメッセージを受信できるようになったので、メッセージを送信するように構成します。 強調表示されているコードを 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 として識別されたメッセージがサーバーに送信されます。 メッセージは、ユーザー名と、テキスト ボックスへのユーザー入力で構成されます。 送信が機能していると、テキスト ボックスの値はクリアされます。

  4. 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 メソッドは、データがクライアントから送信されることを想定しています。 Clients.All 上の SendAsync に対して呼び出しが行われます。 受信したメッセージは、ハブに接続されているすべてのクライアントに送信されます。

アプリのテスト

次の手順で、アプリの動作を確認します。

  1. リリース モードで Webpack を実行します。 パッケージ マネージャー コンソール ウィンドウを使用して、プロジェクト ルートで次のコマンドを実行します。 プロジェクト ルートになっていない場合は、コマンドを入力する前に「cd SignalRWebPack」と入力します。

    npm run release
    

    このコマンドは、アプリの実行中に提供するクライアント側資産を生成します。 資産は、wwwroot フォルダーに配置されます。

    Webpack は、次のタスクを完了しました。

    • wwwroot ディレクトリの内容を消去しました。
    • "トランスコンパイル" と呼ばれるプロセスで TypeScript を JavaScript に変換しました。
    • "縮小" と呼ばれるプロセスで、生成後の JavaScript ファイルのサイズを縮小しました。
    • 処理済みの JavaScript、CSS、および HTML ファイルを src から wwwroot ディレクトリにコピーしました。
    • 次の要素を wwwroot/index.html ファイルに挿入しました。
      • wwwroot/main.<hash>.css ファイルを参照している <link> タグ。 このタグは、終了 </head> タグの直前に置かれます。
      • 縮小された wwwroot/main.<hash>.js ファイルを参照している <script> タグ。 このタグは、終了 </title> タグの直後に置かれます。
  2. [デバッグ]>[デバッグなしで開始] の順に選択して、デバッガーをアタッチせずに、ブラウザーでアプリを起動します。 wwwroot/index.html ファイルは http://localhost:<port_number> で提供されます。

  3. 別のブラウザー インスタンスを開きます (任意のブラウザー)。 アドレス バーに URL を貼り付けます。

  4. いずれかのブラウザーを選択し、[メッセージ] テキスト ボックスにメッセージを入力し、[送信] ボタンを選択します。 次の瞬間、両方のページに一意のユーザー名とメッセージが表示されます。

両方のブラウザー ウィンドウに表示されるメッセージ

その他のリソース