SignalRを利用して、特定のクライアントのみへの通知やユーザーの認証といった機能を実装するには? 実践的なアプリ開発に必須ともいえる機能を紹介する。
本稿は、「ASP.NET SignalRを知る」の続編である。SignalR自体をよく知らない場合は、先に前回の記事を参照してほしい。
ちなみに筆者は、本稿のほかにもSignalR関連で、「ASP.NET SignalRとknockout.jsで実現する次世代Webアプリ | 特集:最新技術を活用したWebアプリとは? - Build Insider」「リアルタイムWebを極める:連載|gihyo.jp」などの記事執筆や、「リアルタイム Web 最前線 〜 Socket.IO & SignalR 徹底解説 [スライド&動画] | 第1回 Build Insider OFFLINE」などのイベント登壇を行っている。またブログ「しばやん雑記」でも、SignalRを中心に情報発信を行っている。
本題に入る前に、まずはSignalRに関する最新のニュースを紹介しておこう。
先々月(5月)の10日にSignalRの最新バージョンとなる1.1.0が公開された。このバージョンではスケールアウトに正式対応しており(次の画面を参照)、Windows Azureなどのクラウド環境での利用が容易となった。
その後、すぐに軽微なバグ・フィックスが行われたバージョンとして1.1.1と1.1.2が公開されたので、1.1.0を今も使っている人はアップデートを行っておくことをお勧めする。
そして、6月3日〜6日(米国時間)に開催されたマイクロソフトのITプロ&エンタープライズ開発者向けカンファレンス「TechEd North America 2013」で行われたDamian Edwards氏のセッションで、SignalR 2.0に関する情報が発表された。すでにスライドやセッション動画はChannel 9で公開されているので(次の画面)、参照していただければと思う。
簡単に内容をまとめると、SignalR 2.0では.NET Framework 4.5のみのサポートとなり、それ以前のフレームワークでは動作しなくなる。これまで以上にOWIN(Open Web Interface for .NET)と密接に結び付くことになり、さらにJavaや.NET Micro Framework向けのクライアント・ライブラリが開発中とのことで、これからもSignalRから目が離せない状況が続くだろう。
ちなみに、SignalR 2.0のベータ2がすでにNuGetで公開されているので、今すぐ試すことができるが、OWINに関係してスタートアップのコードが一部異なっている。実際に試す際にはSignalRインストール後に表示されるreadme.txtのサンプル・コードを参照してほしい。
それでは、本稿の本題に入っていくことにしよう。コードはC#で記述する。
前回でSignalRが提供している双方向通信やハブなど基本的な機能を学ぶことができたと思うが、実際にSignalRを利用してアプリの開発を行っていると、特定のクライアントのみへの通知やユーザーの認証といった機能を実装したいと思うことが多いだろう。
今回はすでにSignalRの基本的な知識があることを前提に、より実践的なアプリ開発に必須ともいえる機能を紹介する。
SignalRではハブのメソッドが呼び出されたときにHubCallerContextクラスのインスタンスを作成する。作成されたインスタンスはHub.Contextプロパティとして公開されているので、ハブのメソッドからはContextプロパティとして簡単に扱うことができる。
以下でHubCallerContextクラスに実装されているプロパティを紹介する。
例によってWebSocketがトランスポートとして使われている場合には、異なるメソッドの呼び出しでも同一のコネクションだと常に同じ値が返されるので、注意する必要がある。
これだけの説明だと分かりにくいが、つまりWebSocketを利用している場合には、接続時にクエリ文字列やクッキーなどをコネクション作成前にセットしておかない限り、Contextプロパティを経由して取得することはできず、途中でクライアントから変更することもできない。
補足しておくと、SignalRのクライアントでは接続時にクエリ文字列を指定できるようになっているので(次のコードを参照)、表示しているページごとに挙動を変えるといったことが容易に実現可能だ。
// hubConnectionメソッドの引数でクエリ文字列を渡す
var connection = $.hubConnection(null, {
qs: { foo: "bar", bar: "baz" }
});
このコード例はJavaScriptのクライアントだが、C#などのクライアントでも同様のAPIでクエリ文字列を指定できる。
前回の説明ではSignalRサーバに接続しているクライアント全てに対してメッセージを送信していたが、SignalRには任意の数のクライアントを1つのグループとして管理する機能が用意されており、その機能を使えば、作成したグループに対してメッセージ送信が行える。このグループ化の機能は、よくあるチャット・ルームのような機能の実装などに最適だ。
そのグループ化だが、「Hubクラスに用意されているGroupsプロパティのAdd/Removeメソッドを使って、グループへの参加と離脱を行うだけ」というシンプルなAPIとなっている。グループに対してメッセージを送信するには、Clients.Groupメソッドを使う。
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
[HubName("group")]
public class GroupHub : Hub
{
// 指定されたグループへ参加する
public void Join(string groupName)
{
Groups.Add(Context.ConnectionId, groupName);
}
// 指定されたグループから離脱する
public void Leave(string groupName)
{
Groups.Remove(Context.ConnectionId, groupName);
}
// 指定されたグループに参加しているクライアントへメッセージを送信する
public void Send(string groupName, string text)
{
Clients.Group(groupName).Receive(text);
}
}
var connection = $.hubConnection();
var group = connection.createHubProxy("group");
// 受信したメッセージをアラート表示する
group.on("Receive", function (text) {
alert(text);
});
connection.start(function() {
// Room1 という名前のグループへ参加する
group.invoke("Join", "Room1");
// Room1 グループへメッセージを送信
group.invoke("Send", "Room1", "sample message");
// Room1 グループから離脱する
group.invoke("Leave", "Room1");
});
SignalRのグループ化機能は非常にシンプルで使いやすいが、残念ながらグループに参加しているクライアントの数やコネクションIDは取得できない。グループに参加しているクライアントの情報が必要な場合には、別途、データベースなどに格納しておく必要がある。
Copyright© Digital Advantage Corp. All Rights Reserved.