, , 2014年3月7日

今まで、幾度か記事にしましたSignalRですが今回はこのSignalRについて、2014年3月現在での導入方法をまとめます。

現在、Owinのバージョンの食い違いにより、SignalRのReadme通りの手順で作業してもエラーになってしまいます。今回はこのエラーをなくし、SiganlRをプロジェクトに導入するところまでを行います。

SignalR

当ブログメンバーのTsubasa Yoshinoが、以前記事に書いている通り、SignalRはASP.NETに双方向通信を提供するオープンソースなライブラリです。これを用いることで、WebSocketやCommet、Server Sent Eventなど様々な手法があった双方向通信を統合することができ、状況によってその手法を切り替えて使うことができます。

“SignalRのご紹介: Tsubasa Yoshino”
“SignalRの紹介2: Tsubasa Yoshino”
“ASP.NET SignalRとSocket.ioを比較してみる: Tsubasa Yoshino”

プロジェクトの作成

まず今回のプロジェクトを作成します。Visual Studioを起動し新しいプロジェクトを作成します。

newproject

ASP.NET Webアプリケーションを選択します。SignalRSampleと名付けました。

次に、テンプレートを選択します。

template

今回は、特にいろいろするつもりではないので、MVCオンリーでそのままOKを押します。

新しいプロジェクトが作成されました。

createdproject

SignalRのインストール

作成したプロジェクトにSignalRをインストールします。

NuGet パッケージ管理からインストールします。

nuget

インストールが完了すると次のようなReadmeがタブで開かれます。

Please see http://go.microsoft.com/fwlink/?LinkId=272764 for more information on using SignalR.

Upgrading from 1.x to 2.0
-------------------------
Please see http://go.microsoft.com/fwlink/?LinkId=320578 for more information on how to 
upgrade your SignalR 1.x application to 2.0.

Mapping the Hubs connection
----------------------------
To enable SignalR in your application, create a class called Startup with the following:

using Microsoft.Owin;
using Owin;
using MyWebApplication;

namespace MyWebApplication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
} 

Getting Started
---------------
See http://www.asp.net/signalr/overview/getting-started for more information on how to get started.

Why does ~/signalr/hubs return 404 or Why do I get a JavaScript error: 'myhub is undefined'?
--------------------------------------------------------------------------------------------
This issue is generally due to a missing or invalid script reference to the auto-generated Hub JavaScript proxy at '~/signalr/hubs'.
Please make sure that the Hub route is registered before any other routes in your application.

In ASP.NET MVC 4 you can do the following:

      <script src="~/signalr/hubs"></script>

If you're writing an ASP.NET MVC 3 application, make sure that you are using Url.Content for your script references:

    <script src="@Url.Content("~/signalr/hubs")"></script>

If you're writing a regular ASP.NET application use ResolveClientUrl for your script references or register them via the ScriptManager 
using a app root relative path (starting with a '~/'):

    <script src='<%: ResolveClientUrl("~/signalr/hubs") %>'></script>

If the above still doesn't work, you may have an issue with routing and extensionless URLs. To fix this, ensure you have the latest 
patches installed for IIS and ASP.NET. 

StartupクラスのConfigurationメソッドでMapSignalRを呼べとのことなのでStartup.csにコードを挿入します。

[csharp]
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(SignalRSample.Startup))]
namespace SignalRSample
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
}
[/csharp]

Hub

SingalRのエンドポイントはハブという単位で固まっています。次に今回使用するHubコントローラを作成します。

プロジェクトに新しくHubsフォルダを作成します。

HubsFolder

Hubsフォルダの中に新しくクラスを追加します。今回はSampleHubとします。

samplehub

Microsoft.AspNet.SignalRとMicrosoft.AspNet.SignalR.Hubsネームスペースをusingで追加します。
次にSampleHubクラスにHubクラスを継承させます。
最後に、属性でHubNameをつけます。

[csharp]
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SignalRSample.Hubs
{
[HubName("sample")]
public class SampleHub: Hub
{
}
}
[/csharp]

これでHubの完成です。ですが、せっかくなのでメソッドも作ります。

[csharp]
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SignalRSample.Hubs
{
[HubName("sample")]
public class SampleHub: Hub
{
public string Through(string message)
{
return message;
}
public void Echo(string message)
{
Clients.Caller.show(message);
}
public void EchoToAll(string message)
{
Clients.All.show(message);
}
}
}
[/csharp]

ThroughとEcho,EchoToAllメソッドを作成しました。メソッドの詳しい説明はクライアントサイドを書いてから述べます。

クライアント再度

Hubの次はクライアントサイドの実装をします。

今回はHome/Indexファイルを書き換えることにします。

まずViews/Home/Index.cshtmlを書き換えます。

[html]
@{
ViewBag.Title = "Home";
}
<div class="jumbotron">
<h1>SignalR Sample</h1>
<p class="lead">SignalR を試してみるよ!</p>
<ul id="messages" class="list-group"></ul>
</div>
<div class="row">
<div class="col-md-4">
<h2>Through</h2>
<input type="text" id="through-message" class="form-control" value="" />
<button id="through-btn" class="btn btn-default">Through</button>
</div>
<div class="col-md-4">
<h2>Echo</h2>
<input type="text" id="echo-message" class="form-control" value="" />
<button id="echo-btn" class="btn btn-default">Echo</button>
</div>
<div class="col-md-4">
<h2>Echo to All</h2>
<input type="text" id="echo2all-message" class="form-control" value="" />
<button id="echo2all-btn" class="btn btn-default">Echo to All</button>
</div>
</div>
@section Scripts{
@Scripts.Render("~/Scripts/jquery.signalR-2.0.2.min.js")
@Scripts.Render("~/signalr/hubs")
@Scripts.Render("~/Scripts/app.js")
}
[/html]

3つのJavaScriptファイルを呼び出します。jquery.signalR-2.0.2.min.jsはsignalrのコアライブラリ、signalr/hubsは先ほど作成したHubから自動生成されるファイルです。app.jsはこれから作成します。

Scriptsフォルダにapp.jsを追加します。

app

[javascript]
(function () {
"use strict";
// hubインスタンス
var sample = $.connection.sample;
// クライアントメソッドの実装
$.extend(sample.client, {
show: function (message) {
_show(message);
}
});
// ヘルパー関数の定義
function _show(message) {
$("<li class=’list-group-item’>" + message + "</li>").appendTo("#messages");
}
function _through() {
var message = $("#through-message").val();
sample.server.through(message)
.done(function (response) {
_show(response);
});
}
function _echo() {
var message = $("#echo-message").val();
sample.server.echo(message);
}
function _echoToAll() {
var message = $("#echo2all-message").val();
sample.server.echoToAll(message);
}
// コネクションを確立させたらクリックイベントにバインド
$.connection.hub.start().done(function () {
$(function () {
$("#through-btn").click(_through);
$("#echo-btn").click(_echo);
$("#echo2all-btn").click(_echoToAll);
});
});
}())
[/javascript]

singalr/hubsをロードすることによって、$.connection.hubNameでHubインスタンスを取得できます。このインスタンスからはsample.server.through(message);のようにサーバーのメソッドをコールできるので、とてもわかりやすいですね!

サーバーからクライアントのメソッドをコールしたときにエラーにならないように6~9行目のようにしっかりsample.clientにメソッドを定義します。
今回コールしているメソッドはshowだけなのでこれを実装します。

起動! → 例外発生!!!

さて、ビルドエラーがないことを確認したら起動してみます。

すると、次のような例外を吐いてアプリケーションが停止してしまいます。

型 'System.IO.FileLoadException' の例外が Microsoft.AspNet.SignalR.Core.dll で発生しましたが、ユーザー コード内ではハンドルされませんでした

追加情報:ファイルまたはアセンブリ 'Microsoft.Owin, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'、またはその依存関係の 1 つが読み込めませんでした。見つかったアセンブリのマニフェスト定義はアセンブリ参照に一致しません。 (HRESULT からの例外:0x80131040)

これは前述したとおり、Owinのバージョンが古いためにおこります。SignalRは最新のOwinで開発されていますが、Visual Studio 2013のテンプレートに同梱されているOwinはバージョンが少し古いため食い違いが起こり例外につながってしまいます。

Owinのアップデートで解決

今のままではアプリケーションが起動しないので、Owinをアップデートして解決します。

パッケージマネージャーコンソールで以下のコマンドを打ちましょう。

PM> Install-Package Microsoft.Owin -Version 2.1.0
PM> Install-Package Microsoft.Owin.Security -Version 2.1.0

インストールが完了したら起動してみます。

launch

無事起動しました!

ThroughやEchoなどを試すときちんとかえって来ます。

補足説明

HubコントローラでClients.Caller.show(message);や、Clients.All.show(message);という風にクライアントメソッドを呼び出していますが、これはそのままの意味で、Callerはサーバーメソッドをコールしたクライアントを指し、AllはHubに接続されて入るすべてのクライアントを指します。なので、EchoToAllをコールすると次のようにすべてのウィンドゥにメッセージが送られます。

echo2all

まとめ

TwitterやFacebookなどのFeedで大活躍中の双方向通信ですが、SignalRを使えばすっきりと実装できそうです。バージョンが上がるごとに書き方が激変するのがたまにキズですが、今後安定してくると願っています。今度がHubグループなどの記事を書こうと思います。

CATEGORIES