xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

Web APIからSignalRへの連携

One ASP.NET Advent Calendar 2013も17日目になりました。すでに3度目の登場です、@xin9leです。ASP.NET/IISのMVPさんよりも多く登場していて場違い感を多少なり感じておりますが、今回も飽き足らずSignalRのネタで攻めます。ご了承ください。

最近の体験談

ここ半年、業務でWPF + SignalR + Web APIな社内システム作りをしていました。その一部で以下のような動きをしている箇所があります。

  • Web API経由でデータベースのテーブルに新規追加/変更/削除を行う
  • そのテーブルの内容をリアルタイムにモニタリングする

Structure

リアルタイムにレコードの変更通知をしようと思ったら、Web APIへのアクセスがあったタイミングでSignalRサービスを呼び出す必要があります。しかしそこは流石のOne ASP.NET。Web APIとSignalRの連携はかなり簡単です。今日はそんな連携の方法をご紹介します。

1. Web APIとSignalRが別サービスのとき

SignalRとWeb APIが別サービスとして配置されている場合は素直にSignalR .NET Clientを利用します。ここではSignalRサービス側に以下のようなHubがあり、「http://localhost:12345」のURLで提供されているものとします。

//--- http://localhost:12345 で提供されているとする
public class MyHub : Hub
{
    public void Broadcast(string value)
    {
        this.Clients.All.Update(value);
    }
}

次にこのHubにアクセスするための接続を確立します。特にネットワーク障害などによる通信断を考慮しなければ、SignalRへの接続はWeb APIのサービスが起動したときに1度だけ行えばOKです。例えばGlobal.asaxに下記のように記述します。

public class Global : HttpApplication
{
    //--- (今は簡単のために) ココでstaticに覚えておく
    private static HubConnection connection = null;
    public static IHubProxy MyHub{ get; private set; }

    protected void Application_Start(object sender, EventArgs e)
    {
        //--- Web APIの初期化
        GlobalConfiguration.Configure(WebApiConfig.Register);

        //--- アプリケーション初期化時に接続を確立
        Global.connection = new HubConnection("http://localhost:12345/");
        Global.MyHub      = connection.CreateHubProxy("MyHub");
        Global.connection.Start().Wait();
    }

    protected void Application_End(object sender, EventArgs e)
    {
        //--- アプリケーション終了時に接続を切る
        Global.connection.Stop();
    }
}

あとはWeb APIへのアクセスがあったときにSignalRサービスへの通知を行うだけです。接続処理が多少メンドクサイですが、特段難しいものではありません。

public class MyController : ApiController
{
    public void Post([FromBody]string value)
    {
        this.HogeRepository.Add(value);           //--- データベースなどへの追加
        Global.MyHub.Invoke("Broadcast", value);  //--- 通知
    }
}

2. Web APIとSignalRが同一サービス内のとき [GlobalHost編]

Web APIとSignalRはひとつのプロジェクト内に共存させることができます。SignalRはアプリケーション単位でグローバルに設定を保持しており、GlobalHostクラスからそれらへのアクセスが可能です。これを利用すれば以下のように通知を行うことができます。

public class MyController : ApiController
{
    public void Post([FromBody]string value)
    {
        //--- データベースなどへの追加
        this.HogeRepository.Add(value);

        //--- 通知
        var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
        context.Clients.All.Broadcast(value);
    }
}

別サービスの場合はSignalR .NET Clientが必要でしたし、Web APIとSignalRサービス間の接続が切れた場合などについての考慮が必要となりますが、同一サービス内だとその辺りは気にする必要がないので非常に簡単です。システム要件にも依りますが、同一サービスとしての運用が可能な場合は採用をオススメします。

3. Web APIとSignalRが同一サービス内のとき [HubController編]

2の方法だけでも全然間違いないのですが、GlobalHost.ConnectionManager...と毎回書くのはメンドクサイです。コントローラーからアクセスするHubが決まっているのであれば、もう少し簡単に書けて良いのではないかと思ったりします。そして、それを叶えてくれる機能がASP.NETにはちゃんと存在しています。

Microsoft ASP.NET Web API SignalR Integration
(2013年12月17日現在ではRC 1)

上記のNuGetパッケージをインストールしてHubController<T>クラスを利用してみます。

public class MyController : HubController<MyHub>
{
    public void Post([FromBody]string value)
    {
        this.HogeRepository.Add(value);     //--- データベースなどへの追加
        this.Clients.All.Broadcast(value);  //--- 通知
    }
}

HubController<T>クラスはApiControllerクラスを継承しています。上記のように継承元を変更するだけでSignalRのHubがあたかもWeb APIのHubControllerに置き換わっただけのような記述ができるようになります。これは簡単!超捗る!

ということで今日はここまで。Web APIとSignalRで楽しいWebサービスづくりをしましょう!明日は縄でクマな@twit_ahfさんです。よろしくお願いします!