Blazor WebAssemblyとSignalRでチャットアプリを作ってみた

はじめに

はじめまして!CCTでエンジニアをしているヒロです。
エンジニアをはじめて3年目に突入したまだまだひよっこな私ですが、BlazorSignalRでチャットアプリを作ってみたので、感想をブログにしてみたいと思います。

作ったアプリは以下のリポジトリで公開しています。
https://github.com/cct-blog/cct-masahiro-morita

構成

構成はざっくりと下の図のような感じになります。

まず、クライアントサイドは今回のブログのメインとなるBlazor WebAssemblyです。サーバーサイドにはASP.NET Coreを用いています。

ASP.NET Core は Web API として JSON の通信に使用していますが、これだけではチャットアプリの要ともいえる他の誰かが送ったメッセージをリアルタイムに画面に反映させることができません。
そこでリアルタイムな通信を実現するために、SignalR というライブラリを使っています。

Blazor

概要

Blazorは 2020 年 5 月に正式リリースした新しめのフレームワークです。C#でクライアントサイドを書くことができ、SPA として動かせます。

.NETで Webアプリケーションを作成できるフレームワークとして、ASP.NET Core の MVC がありますが、これはサーバー側で HTML を生成して静的サイトをブラウザに送っているだけなので、SPA としては機能しません。

つまり、Blazor が出てくるまでは、Vue、React、Angular のような SPA を作る手段は.NET にはありませんでした。
さて、このBlazorですが、Blazor WebAssemblyとBlazor Serverの2種類存在しています。
今回のチャットアプリで使っているのはBlazor WebAssemblyになります。

では、Blazor WebAssemblyとBlazor Serverはどう違うのでしょうか?

Blazor Server

Blazor Serverの要となるのが、先述したSignalRというリアルタイム通信用のライブラリです。
最初に DOM の操作とブラウザ側のSignalRの設定を行う JavaScript を配信しておいて、必要なコンポーネントのデータはSignalRで適宜引っ張ってくる構成になっています。

ブラウザとサーバーが密な通信を行っているので、通信が途切れてしまうと使えなくなってしまうのが欠点ですが、その分、1回に送るデータは少なくてすむので、特に初回にブラウザに送るデータ容量は、後述するBlazor WebAssemblyに比べてかなり小さくてすみます。

Blazor WebAssembly

次は本題のBlazor WebAssemblyと言いたいところですが、まずはWebAssemblyの説明から入ります。

WebAssemblyは文字通り Web でアセンブリを実行できるようにするために生まれた技術です。
ブラウザで動く言語といえば、HTML、CSS、JavaScript だけだったのが、WebAssemblyにコンパイルすればC#やRustなどのほかの言語でも動作させることができるようになりました。

2017年中には主要ブラウザ(Chrome、Firefox、Edge、Safari)に実装され、2020年5月にBlazor WebAssemblyが正式リリースされたことで、C#でも WebAssemblyを使ってSPA を作ることが可能になりました。

ちなみにWebAssemblyにコンパイルして、ブラウザで実行と言っていますが、.NET 5ではWebAssemblyでなく、DLL をブラウザに配信して、ブラウザ側で適宜翻訳しながら実行するという動作をしています。
そのため、動作が JavaScript などと比べて遅く感じてしまいます。
.NET 6 では、AOT(Ahead Of Time)コンパイルがサポートされ、あらかじめ WebAssemblyにコンパイルして配信することができるようになります。これによって動作速度は改善される模様です。

公式ブログには比較動画が上がっているので、興味のある方は確認してみてください。

参考: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#blazor-webassembly-ahead-of-time-aot-compilation

また、WebAssemblyや.NET runtime等を配信している関係上、最初のダウンロードが少し重くなっています。
ブラウザのコンソールに容量を出力してくれていますが、プロジェクト作成段階で 10MB前後あります。
さらに言うと前述した AOTコンパイルを使うと 2倍ほどに容量が増えるようです。

テンプレートからプロジェクトを作ってそのままの状態で初期ローディング時にどの程度の容量がアップロードされるか比較してみました。
Blazor ServerはChrome Developer Toolsのネットワークタブから確認しています。
Blazor WebAssemblyの場合、ネットワークタブに表示される項目が多すぎるので、Blaozr WebAssemblyがコンソールに出力してくれるロード容量を見ています。

Blazor Server

Blazor WebAssembly

Blazor Serverでは500KB前後といったところですが、Blazor WebAssemblyでは、8.83MBもダウンロードしていることが確認できます。

プロジェクトの作成

では、Blaozr WebAssemblyのプロジェクトを作っていきましょう。Visual Studioからでも作成できますが、今回はコンソールから作成していきます。

※ 筆者の環境には.NET 5.0.203がインストールされています。

まず、dotnet new と実行してみてください。作成できるプロジェクトの一覧が表示されます。
今回はBlazor WebAssembly Appです。以下のコマンドでプロジェクトを作成します。

dotnet new blazorwasm --hosted -o ChatApp

hostedオプションはサーバーサイドとして、ASP.NET Coreを使いたいときに指定します。

hosted オプションをつけて生成されるプロジェクトはBlazor WebAssemblyのホスティングとAPS.NET Coreの実行を同じサーバーで行うことを前提にしたものになっています。
クライアントサーバーモデルで実行したい場合は、出来上がったプロジェクトを修正するか、hostedオプションなしで、ASP.NET Coreプロジェクトを別で作成してください。

o オプションはプロジェクトが作成されるフォルダとプロジェクトの名前を指定できます。

コマンドを実行して出来上がったフォルダを見ると、Server、Client、Sharedの3つのプロジェクトが格納されているフォルダができています。
ClientプロジェクトがBlazor WebAssemblyで、ServerプロジェクトがASP.NET Coreです。
最後のSharedプロジェクトですが、これは後ほど説明します。

さて、プロジェクトのひな形もできたので、コマンドラインでServerフォルダに移動して実行してみましょう。

ChatApp\Server > dotnet run

http://localhost:5000にアクセスしてサイドバーからCounterをクリックしましょう。

コンポーネントが再レンダリングされるところを見たいので、Chrome Developer ToolsからPaint flashingを有効にしておきます。

Click Meをクリックすると上のカウンターの数字がインクリメントされるとともに、コンポーネントの色が一瞬変わることが確認できます。

これで、Blazor WebAssemblyのプロジェクトを作成し、SPAとして動作しているところを確認できました。

Blazor WebAssemblyを使うメリット

さて、先ほど少しとばしましたが、テンプレートから作成したプロジェクトには、ClientとServerのほかにSharedというプロジェクトがあります。
このプロジェクトこそがC#で SPA が書けること利点の1つと言えます。

サーバーとクライアントでデータを通信するとき、多くの場合JSONのデータフォーマットを使っていると思います。
しかし、JSONそのままではプログラミング言語からは扱いにくいのでシリアライズ・デシリアライズを行っていると思います。
JavaScriptであれば動的型付け言語なので、気にする必要はありませんが、TypeScriptを用いている場合は できるだけ型は用意したいと思うのではないでしょうか。
しかし、サーバー側とクライアント側で同じJSONの型を実装するのは手間ですし、ミスをしてバグを作る可能性が増えてしまいます。

この問題を解決してくれるのが、Sharedプロジェクトです。
SharedプロジェクトはBlazor WebAssemblyとASP.NET Coreプロジェクトの両方から参照されており、ここに通信で使用する型情報を格納しておけば、どちらのプロジェクトからも使うことができます。
これは、サーバー側とクライアント側を同じ言語で書ける強みです。

Blazor WebAssemblyを使ってみた感想

JavaScriptの SPAフレームワークの中では、Reactを触る機会が多いですが、Reactに比べて書き始めるまでのハードルがだいぶ低いように感じました。
プロジェクト作成後はコーディングスタイルの統一のために.editconfigファイルを追加しただけで、それ以外に追加で行うことなく、そのまま実装に移ることができました。
準備段階の手軽さは Blazorの利点の1つだと思います。

次にSignalRを使ったリアルタイム通信について説明します。

SignalR

概要

SignalRはリアルタイム通信を行うためのライブラリです。
サーバーサイドはASP.NET Core限定ですが、クライアントサイドは.NET以外からも選ぶことができます。

クライアントサイドのライブラリが提供されているのは以下の通りです。
ライブラリによっては一部機能が使えなかったりするので、実現したいことがSignalRでできるかどうかよく検討して使ってください。

  • .NET
  • JavaScript
  • Java

※ 他にSwiftとC++のライブラリがありますが、公式にサポートはされていません。

参考: https://docs.microsoft.com/ja-jp/aspnet/core/signalr/supported-platforms?view=aspnetcore-5.0#unsupported-clients

リアルタイム通信と言っていますが、双方向通信という言い方のほうがよく使われていると思います。
双方向通信を実現するためにはいくつかの方法がありますが、SignalRでは、WebSocket、Server Sent Event (SSE)、ロングポーリングの3つが実装されています。
こちらで、どの通信方式を選択するかを選ぶこともできますが、基本は SignalRが最適な通信方法を選択してくれるようになっているので、通信方法に気を使うことなく双方向通信が実現できます。

ただし、IISではWebSocketを有効化する必要があるなど、想定していたものとは違う通信方式が採用されている可能性があるので、1度は確認してみることをおすすめします。

参考: https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/websockets?view=aspnetcore-5.0#iisiis-express-support

SignalRの構成

SignalRでは、クライアントとサーバーでお互いに登録したメソッドをもう片方から呼ぶような形で実装していきます。

まず、ASP.NET Coreの側ですが、Hubクラスを継承したクラスを作成します。
このクラスに追加したメソッドがクライアントから呼ばれることになります。
HubクラスにはClientsというプロパティが含まれています。
このプロパティに通信用のプロパティやメソッドが実装されているので、Clientsプロパティ内のメソッドからクライアント側のメソッドを呼び出して、通信することができます。

クライアント側からはハブコネクションビルダーからハブコネクションクラスのインスタンスを作成し、そこにサーバーから呼び出されるメソッドを追加して活きます。
.NETのクライアントではOnメソッドとして実装されており、第1引数にサーバーで呼び出す時のメソッド名、第2引数に実処理を設定します。
ハブコネクションクラスにもサーバー側のメソッドを呼び出すためのメソッドが実装されているので、それを呼び出してサーバー側と通信します。

ASP.NET Coreで認証を有効にしている場合、Hubクラスに実装したメソッドにAuthorize属性をつけることで、認証に成功したユーザーのみサーバー側のメソッドを呼べるようにすることもできます。

また、サーバー側から特定のユーザーに向けてメッセージを送ることもできます。

リアルタイムに情報を反映したい機会は多いですが、WebSocketを自分で実装するとなると時間もかかりますし、保守も大変です。
しかし、SignalRを使えば簡単にリアルタイム通信が実現できてしまいます。
リアルタイムに通信がしたい!と思ったら、ぜひSignalRを検討してみてください。

おわりに

最後まで読んでいただきありがとうございました。

Blazor WebAssemblyをはじめて触りましたが、Visual Studioの強力なサポートを得られることもあり、JavaScriptのSPAフレームワークよりすんなり入ることができました。
ホットリロードにも.NET 6で対応予定ですので、ますますBlazor WebAssemblyを使った開発体験は向上すると思います。

参考: https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/

今回は紹介していませんが、認証認可についてもテンプレートでかなりの部分を対応してくれます。
.NETには便利なツールがたくさんあるので、どんどん活用してきたいですね!

最近の記事

  • 関連記事
  • おすすめ記事
  • 特集記事

アーカイブ

カテゴリー

PAGE TOP