SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

.NET最新版でASP.NET Core

ASP.NET Coreでリアルタイム通信を実現! SignalRの使い方

.NET最新版でASP.NET Core 第12回

  • X ポスト
  • このエントリーをはてなブックマークに追加

チャットアプリケーションを構築する

 ここから、アプリケーションを作っていきましょう。このアプリは、サーバに接続した複数のクライアント(Webブラウザ)間で、入力した名前とメッセージを共有してやり取りするというものです(図3)。クライアントが送信したメッセージが、ハブを介して全クライアントに送信されるという流れだけに絞って見るために、メッセージの保存は行いません。アプリケーションの名前はSignalRChatSampleとします。

図3:チャットルームアプリの外観
図3:チャットルームアプリの外観

 クライアントの実装は、リアルタイム性に優れてHTML(Razorページ)との連携も容易なJavaScriptを使用します。これを踏まえたアプリケーションの構成は図4の通りです。ハブ(C#)、クライアント(JavaScript)、Razorページ(.cshtml)、そしてスタートアップコード(Program.cs)のそれぞれを作成、修正していくことで目的のアプリケーションを作成します。以降の説明において、適宜図4を参照してください。

図4:チャットルームアプリの構成
図4:チャットルームアプリの構成

アプリケーションの作成

 SignalRアプリケーションのためのテンプレートは用意されていませんので、ここでは通常のRazor Pagesアプリケーションを作成します。以下のdotnet newコマンドで作成します。

% dotnet new webapp -o SignalRChatSample -f net7.0

 このままでは第2回で紹介したRazor Pagesアプリケーションと変わりません。ただちに実行できますが、既定のページが表示されるだけです。ここに、SignalRによるチャットアプリのためのライブラリ、ファイル、コードを追加していきます。なお、-fオプションは本稿作成時点での最新バージョンである.NET 7をターゲットにする指定です。

クライアントライブラリのインストール

 SignalRをクライアントから利用するためのライブラリをプロジェクトにインストールします。インストールには、ASP.NET CoreのクライアントライブラリマネージャであるLibManを使用します。LibManがインストールされていない場合(本連載では初出)、以下のコマンドでLibManのCLIツールをインストールします。

% dotnet tool install -g Microsoft.Web.LibraryManager.Cli
次のコマンドを使用してツールを呼び出せます。libman
ツール 'microsoft.web.librarymanager.cli' (バージョン '2.1.175') が正常にインストールされました。

 ここで、プロジェクトのルートであるSignalRChatSampleフォルダに移動して、SignalRのためのクライアントライブラリをLibManのCLIツールであるlibmanコマンドでインストールします。

% libman install @microsoft/signalr@latest -p unpkg -d wwwroot/js/signalr --files dist/browser/signalr.js
ファイル https://unpkg.com/@microsoft/signalr@latest/dist/browser/signalr.js をダウンロードしています...
wwwroot/js/signalr/dist/browser/signalr.js はディスクに書き込まれました
ライブラリ "@microsoft/signalr@latest" が "wwwroot/js/signalr" にインストールされました

 libmanのinstallサブコマンドで、引数で指定する(この場合は@microsoft/signalr)npmパッケージをインストールします。指定したオプションは以下の通りです。

  • -p:CDN(Content Delivery Network)などのプロバイダの指定(この場合はunpkg)
  • -d:書き込み先(wwwroot/js/signalr)の指定
  • --files:ダウンロードファイル(dist/browser/signalr.js)の指定

ハブの作成

 ここからはコードの作成と修正です。まずは、ハブを新規に作成します。プロジェクトのルートにHubフォルダを作成し、そこにChatRoomHub.csファイルを作成します。そして、このファイルにリストの内容を記述します。

リスト:Hub/ChatRoomHub.cs
using Microsoft.AspNetCore.SignalR;	(1)

namespace SignalRChatSample.Hubs	(2)
{
    public class ChatRoomHub : Hub	(3)
    {
        public async Task TransmitMessage(string sender, string message)	(4)
        {
            DateTime dt = DateTime.Now;
            await Clients.All.SendAsync("ReceiveMessage", sender, message, 
                                        dt.ToString("yyyy/MM/dd HH:mm:ss"));	(5)
        }
    }
}

 (1)は、SignalRのサーバライブラリの使用のために必要な宣言です。このように、SignalRのサーバ側のライブラリはASP.NETのコアライブラリに含まれており、宣言だけで利用できます。

 (2)では、ハブクラスを含める名前空間SignalRChatSample.Hubsを宣言しています。アプリケーションが複数のハブクラスを持つなら、同じ名前空間に配置するようにします。また、後述するProgram.csファイルでこの名前空間を参照するので、同じ名前空間になるようにします。

 (3)では、チャットルームのハブクラスを定義しています。ハブクラスは、Hubクラスを継承することになっています。

 (4)では、非同期メソッドTransmitMessageを定義しています。このメソッドは、クライアントからのリクエストに応じて呼び出されて、必要な処理を行います。(5)は、この処理内容であり、SendAsyncメソッドが実行されます。Clientsオブジェクトは文字通りクライアントを意味し、Allプロパティはその全てを表します。つまり、全クライアントに対して非同期にクライアントメソッド名"ReceiveMessage"とともに引数(名前、メッセージ、現在日時)を全て送信せよという意味になります。"ReceiveMessage"はクライアントのメソッドに相当し、この名前で定義されたメソッドが各クライアントで呼び出されます。

[NOTE]送信先の選択

 このサンプルでは、送信者を含めた全クライアントにメッセージを送信しています(All.SendAsyncメソッド)。このほかに、送信者自身(Caller.SendAsyncメソッド)、送信者以外(Others.SendAsyncメソッド)に送信するメソッドも用意されており、用途に応じて使い分けることができます。

チャットルームページの作成

 チャットルームのページは、アプリケーションのルートページすなわちIndex.cshtmlに置くことにします。既定では、このページはメニューとタイトルだけなので、そこにチャットの入力フォームとチャット履歴を表示するリストを配置します。

リスト:Pages/Index.cshtml
@page
@{
    ViewData["Title"] = "チャットルーム";
}
<div class="container">
    <div class="row p-1">		(1)
        <div class="col-2">お名前</div>
        <div class="col-6"><input type="text" class="w-50" id="sender" /></div>
    </div>
    <div class="row p-1">
        <div class="col-2">メッセージ</div>
        <div class="col-6"><input type="text" class="w-100" id="message" /></div>
    </div>
    <div class="row p-1">
        <div class="col-8">
            <input type="button" id="submit" value="送信!" disabled />
        </div>
    </div>
    <div class="row p-1">
        <div class="col-8">
            <hr />
        </div>
    </div>
    <div class="row p-1">		(2)
        <div class="col-8">
            <ul id="messages" class="list-unstyled"></ul>
        </div>
    </div>
</div>
<script src="~/js/signalr/dist/browser/signalr.js"></script>		(3)
<script src="~/js/chatroom.js"></script>

 (1)は、送信者の名前とメッセージと送信ボタンからなるフォームとなっています。名前、メッセージ、ボタンのid属性はそれぞれ後述するJavaScriptコードから参照されます。

 (2)は、チャット履歴の表示場所です。表示にはulタグ(リストマーカーなし)を使用しています。このid属性も、後述するJavaScriptコードから参照されます。

 (3)からは、それぞれSignalRのクライアントライブラリsignalr.jsと、後述するクライアントコードであるchatroom.jsを、それぞれ読み込んでいます。

クライアントコードの作成

 上記のページで読み込まれるchatroom.jsをwwwroot/jsフォルダ以下に新規に作成し、クライアントのコードを書いていきます。

リスト:wwwroot/js/chatroom.js
"use strict";

let conn = new signalR.HubConnectionBuilder().withUrl("/chatRoomHub")
    .build();		(1)

conn.start().then(() => {	(2)
    document.getElementById("submit").disabled = false;
}).catch((err) => {
    return console.error(err.toString());
});
    
conn.on("ReceiveMessage", (sender, message, dt) => {	(3)
    let li = document.createElement("li");
    document.getElementById("messages").insertAdjacentElement('afterbegin', li);
    li.textContent = `${dt} [${sender}] ${message}`;
});

document.getElementById("submit").addEventListener("click", (e) => {	(4)
    let sender = document.getElementById("sender").value;
    let message = document.getElementById("message").value;
    conn.invoke("TransmitMessage", sender, message).catch((err) => {
        return console.error(err.toString());
    });
    e.preventDefault();
});

 (1)では、ハブ接続オブジェクトを生成しています。withUrlメソッドは、引数のURLで接続するハブクラスの指定を行います。URLとハブクラスのマッピングは、後述するProgram.cs中のMapHubメソッドによって行います。

 (2)は、コネクション開始時にコールバックされる関数です。処理内容は、ボタンを有効化するだけですが、必要に応じて入力フォームの準備などの処理を行わせることができます。

 (3)は、ハブのSendAsyncメソッドの引数で指定したクライアントメソッド"ReceiveMessage"に対応するコールバック関数の定義です。処理内容は、3つの引数(送信者名、メッセージ、現在日時)を受け取って、それをもとにli要素を生成してフォームのチャット履歴の上端に挿入します。

 (4)は、ボタンクリック時のイベントハンドラの定義です。名前、メッセージを取り出し、ハブ接続オブジェクトのinvokeメソッドを使って、ハブのTransmitMessageメソッドを呼び出します。

スタートアップコードの修正

 最後に、スタートアップコードであるProgram.csファイルにSignalR利用のための追記を行います。

リスト:Program.cs
using SignalRChatSample.Hubs;		(1)

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();		(2)
…略…
app.MapRazorPages();
app.MapHub<ChatRoomHub>("/chatRoomHub");	(3)

app.Run();

 (1)には、ハブのコードと同様の名前空間を指定します。これは、(3)でハブクラスの名前を参照するためです。

 (2)では、SignalRをサービスとして登録しています。これは、SignalRを使うアプリケーションで必須の指定です。

 (3)では、エンドポイント/chatRoomHubへのリクエストをChatRoomHubクラスにマップしています(JavaScriptからの呼び出しなので相対パスでよい)。この記述により、/chatRoomHubをエンドポイントとする通信はChatRoomHubクラスによって処理されるようになります。ここで指定するエンドポイントが、chatroom.jsファイルの(1)においてwithUrlメソッドの引数にもなっていることを思い出してください。なお、以下のようにMapHubメソッドの引数にoptions.Transportsを指定することで、使用するトランスポートを明示的に指定することができます(この場合はWebSocketとサーバ送信イベント)。

app.MapHub<ChatRoomHub>("/chatRoomHub", options => {
    options.Transports = HttpTransportType.WebSockets |
                         HttpTransportType.ServerSentEvents;
});

動作確認

 ここでdotnet watchコマンドを実行すると、チャットルームアプリを起動し、Webブラウザを自動的に開きます。図3のようにWebブラウザのウィンドウを複数開き、相互にメッセージを入力して同じチャット履歴が表示されれば成功です。

% dotnet watch
dotnet watch 🚀 Started
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5006
…略…

まとめ

 今回は、ASP.NET CoreにおけるSignalRの概要と、シンプルなチャットルームを実現するアプリケーションの作成を通じてSignalRの機能を見てきました。非常にシンプルなコードで、リアルタイム性の必要なアプリケーションを構築できることをご理解いただけたのではないかと思います。

 今回を以て、最新.NETにおけるASP.NET Coreの利用を紹介する連載は終了です。.NET 6を対象に始まった連載も、その途中で.NET 7のリリースを迎えたように、.NETは常に進化しています。.NETにおけるWeb開発に興味を持った方、今のASP.NET Coreの姿を知りたいという方にとって少しでも有益な情報をお届けできたなら幸いです。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
.NET最新版でASP.NET Core連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18494 2023/11/01 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング