C#、ASP.NET、TypeScript、Angular を中心にプログラミングに関した話題を諸々。
by @jsakamoto
検索
リンク
北海道のITコミュニティ - CLR/H 無聊を託つ
タグ
カテゴリ
最新の記事
認証が有効な Blazor ..
at 2020-06-20 15:45
ASP.NET Core で..
at 2020-05-27 21:59
(エディタのXML自動整形で..
at 2020-04-13 21:30
MSBuild の Writ..
at 2020-03-23 22:24
Twitter 認証を実装し..
at 2020-02-17 19:04
最新のコメント
Hyper-vのマウス問..
by macin at 15:54
Windows Upda..
by mtakama at 12:06
同じ問題で悩んでいました..
by yonas at 12:32
Mac(Chrome)で..
by Macユーザー at 00:46
すみません、記事書きかけ..
by developer-adjust at 22:36
続きはないんですか?
by hanamo at 13:03
助かりました。ありがとう..
by あああ at 21:32
いえす、F#! F#! :)
by developer-adjust at 21:46
F#はAzure Not..
by 幻のK泉さん at 18:37
> 今、Setcronj..
by developer-adjust at 08:25
記事ランキング
最新のトラックバック
Web API における..
from 松崎 剛 Blog
[Other]Code2..
from KatsuYuzuの日記
Developer @ ..
from .NET Clips
asp.netでrail..
from 4丁目より
F#でASP.NET M..
from ナオキにASP.NET(仮)
[B!] これはいい h..
from Twitter Mirror
[報告] Microso..
from .NET Clips
Developer @ ..
from .NET Clips
[F#]F# でブログア..
from 予定は未定Blog版
ASP.NET MVC ..
from ナオキにASP.NET(仮)
以前の記事
2020年 06月
2020年 05月
2020年 04月
2020年 03月
2020年 02月
2020年 01月
2019年 12月
2019年 11月
2019年 10月
2019年 09月
2019年 08月
2019年 07月
2019年 06月
2019年 05月
2019年 04月
2019年 03月
2019年 02月
2019年 01月
2018年 12月
2018年 11月
2018年 10月
2018年 09月
2018年 08月
2018年 07月
2018年 06月
2018年 05月
2018年 04月
2018年 03月
2018年 02月
2018年 01月
2017年 12月
2017年 11月
2017年 10月
2017年 09月
2017年 08月
2017年 07月
2017年 06月
2017年 05月
2017年 04月
2017年 02月
2017年 01月
2016年 12月
2016年 11月
2016年 10月
2016年 09月
2016年 08月
2016年 07月
2016年 06月
2016年 05月
2016年 04月
2016年 03月
2016年 02月
2016年 01月
2015年 12月
2015年 11月
2015年 10月
2015年 09月
2015年 08月
2015年 07月
2015年 05月
2015年 04月
2015年 03月
2015年 02月
2015年 01月
2014年 12月
2014年 11月
2014年 10月
2014年 09月
2014年 08月
2014年 06月
2014年 04月
2014年 03月
2014年 02月
2014年 01月
2013年 12月
2013年 10月
2013年 09月
2013年 08月
2013年 07月
2013年 06月
2013年 05月
2013年 04月
2013年 03月
2013年 02月
2013年 01月
2012年 12月
2012年 11月
2012年 10月
2012年 09月
2012年 08月
2012年 07月
2012年 06月
2012年 05月
2012年 04月
2012年 03月
2012年 02月
2012年 01月
2011年 12月
2011年 11月
2011年 10月
2011年 09月
2011年 08月
2011年 07月
2011年 06月
2011年 05月
2011年 04月
2011年 03月
2011年 02月
2011年 01月
2010年 12月
2010年 11月
2010年 10月
2010年 09月
2010年 08月
2010年 07月
2010年 06月
2010年 05月
2010年 04月
2010年 03月
2010年 02月
2010年 01月
2009年 12月
2009年 10月
2009年 09月
2009年 07月
2009年 06月
2009年 05月
2009年 04月
2009年 03月
2009年 02月
2009年 01月
2008年 12月
2008年 11月
2008年 10月
2008年 09月
2008年 08月
2008年 07月
2008年 06月
2008年 05月
2008年 04月
2008年 03月
2008年 02月
2008年 01月
2007年 12月
2007年 11月
2007年 04月
2007年 03月
2007年 02月
2007年 01月
2006年 11月
2006年 10月
2006年 09月
2006年 08月
2006年 07月
ファン
ブログジャンル
画像一覧
2017年 10月 30日

ASP.NET Core SignalR v.1.0 Preview2 Final で Web API のアクションメソッド内など任意の箇所からクライアントにプッシュ送信する方法

ASP.NET Core SignalR とは

C# などの .NET 言語で実装する Web アプリのフレームワークである ASP.NET。
その ASP.NET フレームワーク上で、ブラウザ~サーバー間のリアルタイム双方向通信を実現するライブラリが SignalR である。

Node.js でいうところの Socket.IO に相当する、といえば話のとおりが良いだろうか。
(※もっとも、両者はそれぞれの思想や特性があるのであまり似てはいないとのこと。サーバー側からブラウザ側へ "プッシュ" する機能をカバーするという点で類似のライブラリとして話題にされるようだ。)

その SignalR だが、ASP.NET の新世代である ASP.NET Core 版もちゃんと存在する。

ただ、この記事を書いている時点では、ver.1.0.0 Alpha2 リリース最終版というバージョンが、プレリリース版として公開されているのみ。
まだ正式の初版リリースには至っていない。

Alpha1 から Alpha2 に更新されたときも、若干の破壊的変更があったようで、いかにもまだ Alpha 段階という感じ。

そんな αバージョン時点での、ASP.NET Core SignalR のお話。

ASP.NET Web API から SignalR への連携

SignalR を使った Web アプリを実装しているときに、サーバー側とクライアント側との間の通信技術が SignalR 一択である場合は、とくに困ることなく教科書どおりに実装すればよい。

しかし時折、その同一 Web アプリ上で、任意の HTTP クライアントに対する公開 Web API を搭載したい、なんてことがある。

で、えてして、その Web API で何やらが POST されたら、SignalR 経由で他のクライアントにプッシュ通知したい、なんて話になったりする。

さて、ASP.NET (ASP.NET Core じゃないほう) 時代、Web API の実装にあたっては、ASP.NET WebAPI が提供する ApiController クラスの派生クラスで API コントローラクラスを実装する。
Web API の要求は、この API コントローラクラスのメソッドに紐づけられる寸法だ。

ということで、Web API への POST を、SingalR によるプッシュ通信につなげるには、(POSTを受け付ける) API コントローラクラスのメソッド内で、SignalR における Hub クラスのコンテキストを手に入れる必要がある。


これを実現するには、下記のように書けば OK だ。
using Microsoft.AspNet.SignalR;
// FooHub という SignalR Hub クラスが定義済みだとして:
...
public IActionResult Post(...) {
...
var connectionManager = GlobalHost.ConnectionManager;
var hubContext = connectionManager.GetHubContext<FooHub>();
// ここで hubContext.Clients.All.hoge(...) とかできる
..
すなわち、ASP.NET SignalR が提供する GlobalHost クラスの ConnectionManager という静的プロパティを経由して、Hub コンテキストを入手可能という仕組みだ。

静的プロパティは、少なくともスコープの観点では事実上のグローバル変数のようなものだろう。
それでどんなシチュエーションからでも参照・入手可能ということになる。

ちなみに他にも、"HubController<T> を使う" という技法もあるらしい (2013年の記事だが、下記参照)。

ASP.NET Core SignalR からは GlobalHost クラスは廃止

...と、まぁ、ここまでは ASP.NET Core じゃない、元祖(?) ASP.NET における話。

実は ASP.NET Core 上の SignalR では、GlobalHost クラスは廃止された模様だ。

では ASP.NET Core の SignalR において、"API コントローラクラスのメソッド内から SignalR でのサーバー側からのプッシュ送信" を行うにはどうしたらよいか?


ASP.NET Core では、単純に、コントローラクラスのコンストラクタ引数に必要とするサービスインスタンスを渡してくれる、"DI (Dependency Injection, 依存性注入)" の仕組みで、任意の Hub のコンテキストが手に入る。

すなわち、コントローラクラスのコンストラクタの引数に、使用したい Hub コンテキスト型の引数を設けておけば、それだけで、ASP.NET Core MVC がそのコントローラクラスをインスタンス化するときに、その Hub コンテキストをコンストラクタ引数に入れてくれるのだ。

あとはコンストラクタに引数として渡された Hub コンテキストを、コントローラ自身のプロパティにキャッシュしておいて、アクションメソッド内から利用すれば OK だ。

具体的には下記コードとなる。
public class BarController : Controller {

private IHubContext<FooHub> FooHubContext { get; }

public BarController(IHubContext<FooHub> fooHub) {
this.FooHubContext = fooHub;
}

public IActionResult Post(...){
// ここで this.FooHubContext.Clients.All.hoge(...) とかできる
}
}
SignalR の Hub コンテキストだけ特別扱いということなく、他の各種サービスインスタンスと同じように、DI の仕組みで参照が手に入るのは、すっきりしていて覚えやすい。

また、かつての ASP.NET SignalR における GlobalHost.ConnectionManager のような static プロパティ = 実質のグローバル変数を参照することがなくなったので、単体テストもより書きやすくなる。

Web API コントローラクラス"以外"からの SignalR 連携

さてところで、Web API のコントローラクラスではなく、もっとほかのトリガーをもとに SignalR のサーバー側からクライアント側へのプッシュ送信を行うにはどうしたらよいだろう?

例えば ASP.NET Core をセルフホスティングしたプロセス上で、GPIO ピンに対する入力を監視し、入力に変化があったら SignalR でクライアントに通知する、などの実装である。

先述のとおり、(ASP.NET Core じゃない) ASP.NET SignalR では、事実上のグローバル変数 = GlobalHost.ConnectionManager をどこからでも参照できてた。
それで上記例のような案件でも GlobalHost.ConnectionManager 経由でクライアントへのプッシュ送信が記述できた。

しかし ASP.NET Core の SignalR では、既に説明したとおり、GlobalHost.ConnectionManager のような実質のグローバル変数は、もう存在しない。

ではどうするか?


私が思いついたのは、ASP.NET Core の DI の仕組みで任意の Hub クラスのコンテキストが手に入るのだから、きっと ASP.NET Core の DI の仕組みに乗っかればいいはず、というアイディアだ。


ただ残念なことに、現時点の私の知識・技量では ASP.NET Core における DI の仕組みに疎い。
ASP.NET Core MVC がコントローラクラスのインスタンス生成をどうやってるか、同じことを自分のコードで真似するにはどうしたらよいか (先の例でいうと、GPIO の監視をつかさどるクラスを new するときに、どうやって依存性の注入を行うのか) がわかってないのだ。

それでも自分がわかっている範囲で、そこそこの解決策はある。

ASP.NET Core におけるエントリポイント、Startup クラスにおいて、アプリ起動時に呼び出される Configure() メソッドの引数に渡される IApplicationBuilder オブジェクトを参照すれば、DI の仕組みで注入可能なサービスインスタンスを入手可能である。

ということで、Startup クラスの Configure() メソッド内で下記のとおり実装すれば、任意の Hub コンテキストの参照を入手可能だ。
using Microsoft.AspNetCore.SignalR;
// FooHub という SignalR Hub クラスが定義済みだとして:
...

public void Configure(IApplicationBuilder app) {
...
var hubContext = app
.ApplicationServices
.GetService<IHubContext<FooHub>>();

こうして入手した Hub コンテキストを、目的のオブジェクトに何らかの手段で引き渡せば OK だ。
(先の例でいえば、この Configure メソッド内で GPIO 監視クラスを new し、そのコンストラクタに Hub コンテキストを引き渡す設計にする、などの実装が考えられる)

まとめ

ASP.NET Core 時代の SignalR では、"DI (Dependency Injection, 依存性注入)" の仕組みで、Hub コンテキストを入手可能だ。

特に ASP.NET Core MVC/Web API コントローラクラスであれば、そのコンストラクタに IHubContext<T> 型の引数を用意すれば、そのコントローラクラスがインスタンス化 (new) されるときに、このコンストラクタ引数に T 型の Hub のコンテキストが渡される。

ASP.NET Core の DI の仕組みに乗っかれない場合でも、最悪、Startup の Configure() メソッドのタイミングで、IApplicationBuilder オブジェクト経由で、任意の Hub コンテキストを入手可能だ。
 

by developer-adjust | 2017-10-30 22:29 | .NET | Comments(0)