.NET Conf 2019のSignalRのデモを動かす
こちらの動画を見ながら,1つ目のデモと同じリアルタイムチャットアプリを作る.
ソースコードは公開されていないが,画面を凝視すればコードが読めるし,説明は丁寧でわかりやすい.
環境
- Windows 10 pro 1906
- Visual Studio 2019 16.4 preview1
- dotnet core 3.0.100
プロジェクトの作成
ASP.NET Core WebApplicationのプロジェクトを,Emptyなテンプレートを使ってをdotnet core 3.0
で作成する.
Starup.csでSignalRを使用する設定を行う
ある機能をASP.NET Coreで有効にする場合は,おなじみのStartup.ConfigureServicesメソッドで使うサービスを登録し,Startup.Configureメソッドで使用することを明記する.
SignalRの場合,以下のようにservices.AddSignalR()
をConfigureServicesに加え,Configureメソッド内のUserEndpoints
内でendpoints.MapHub<Chat>("/chat");
とルーティングを設定する.
... public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews();//クライアントのページも用意するので、今回は必要。 services.AddSignalR(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles();//クライアントサイドのJavaScriptライブラリにアクセスするために必要. app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chathub"); }); }
サーバーサイドの実装
上でChatHub
クラスを実装していないのでそれを作る.
ここでMapHubクラスにわたすクラスはHub
クラスを継承したもので,文字通り通信のハブとなる役割を担うものである.
以下のようなHubを作成する.
using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRCorePractice { internal class ChatHub:Hub { public Task Send(string message) { return Clients.All.SendAsync("Receive",message); //クライアント側のreceiveメソッドを呼ぶ. //クライアント側のconnection.Onメソッドで指定しているもの } } }
このChatHubクラスにSendという名前のメソッド定義することにする.
これはクライアントがメッセージを送る際に呼び出すメソッド.
このメソッドは,Clients.All.SendAsync("Receive",message);
とあるように,全てのクライアントに対して送られてきたデータ(message)を全てのクライアントに対して送信する.
ネットワーク越しにメソッドを呼び出すとかRPCのようだ.
サーバーサイドはこれでおしまい.
クライアントサイドの実装
クライアント側SignalRライブラリのインストール
クライアントサイドではJavaScriptでサーバーとSignalR通信を行う.
そのため,JavaScriptのSignalRライブラリが必要なのでインストールする.
@microsoft/signalrというnpmパッケージをインストールする.npmパッケージのインストールについては以下の記事を参考にする.
@microsoft/signalr 3.0.0をインストールする.
(余談:@aspnet/signalrは古いパッケージ)
signalrのインストール後に以下のようなメッセージが表示されるかもしれないけれど,これはTypeScriptを使う場合のサジェストなので,今回は無視しておく.JavaScriptを使うので.
現時点でソリューションのフォルダ構成はこんな状態.
チャットページの実装
シンプルな作りにするために,wwwroot直下にindex.html
を作成する.
index.htmlについての説明はコメントして書き込んだ.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <!-- 入力欄とボタン --> <input type="text" id="message" /> <input type="button" value="Send" id="send" /> <!-- ここにメッセージが追加されていく --> <ul id="messages"></ul> <!-- クライアント側のSignalRライブラリを読み込む --> <script src="lib/@microsoft/signalr/dist/browser/signalr.js"></script> <!-- サーバーにメッセージを送る処理 --> <script type="text/javascript"> (async function () { var connection = new signalR.HubConnectionBuilder() .withUrl('/chathub') .build(); //サーバーからのメッセージ受信時の処理 connection.on('receive', function (message) { //サーバー側から呼び出されるメソッド名を文字列で //messageにサーバーからのデータが入ってくる //li要素を新規作成 var li = document.createElement('li'); //li要素のテキストとしてmessageをセットして追加 li.innerText = message; document.getElementById('messages').appendChild(li); }); //Sendボタンが押されたときの処理.Clickイベントにイベントハンドラを設定している. document.getElementById('send').addEventListener('click', async function () { //テキストボックス内の文字列を取得 var value = document.getElementById('message').value; //サーバー側のSendメソッドにvalue(= message)を渡して実行する. //文字列でサーバー側のsendメソッドを指定する await connection.invoke('send', value); }); await connection.start(); })(); </script> </body> </html>
これで完成.
実行
Ctrl+F5で実行する.
https://localhost:<<ポート番号>>/index.html
にアクセスすると,以下のような味気のないシンプルなページが表示される.
同じページを複数開いて,片方で入力して送信したメッセージが全てのページに表示されることが確認できる.
注意点
HubのURLの文字列が同じであること.
//サーバー側 app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chathub");//ここと }); //クライアント側 var connection = new signalR.HubConnectionBuilder() .withUrl('/chathub')//ここ .build();
SignalRで呼び出すサーバー側メソッドは文字列で指定する.
例えば,ChatHubクラス内にSend
メソッドを作ったとして,クライアント側からこのメソッドにメッセージを送ったり,このメソッド返す値を受け取る場合は,このメソッド名の文字列(この例では「send」.大文字小文字は無視される)を使う.
//サーバー側 internal class ChatHub:Hub { public Task Send(string message) { return Clients.All.SendAsync("Send", message); } } //クライアント側 //サーバーからのメッセージ受信時の処理 connection.on('send', function (message) { //****ここ*** //messageにサーバーからのデータが入ってくる //li要素を新規作成 var li = document.createElement('li'); //li要素のテキストとしてmessageをセットして追加 li.innerText = message; document.getElementById('messages').appendChild(li); }); //Sendボタンが押されたときの処理.Clickイベントにイベントハンドラを設定している. document.getElementById('send').addEventListener('click', async function () { //****ここ*** //テキストボックス内の文字列を取得 var value = document.getElementById('message').value; //サーバー側のSendメソッドにvalue(= message)を渡して実行する. //文字列でサーバー側のメソッドを指定する await connection.invoke('send', value); //****ここ*** });