.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); //****ここ***
});