チュートリアル:Blazor Server チャット アプリを構築する
このチュートリアルでは、Blazor Server アプリの構築と変更を行う方法について説明します。 学習内容は次のとおりです。
- Blazor Server アプリ テンプレートを使用して簡単なチャット ルームを作成する。
- Razor コンポーネントを操作する。
- Razor コンポーネントのイベント処理とデータ バインディングを使用する。
- Visual Studio で Azure App Service にすばやくデプロイする。
- ローカルの SignalR から Azure SignalR Service に移行する。
開始の準備はできていますか?
前提条件
- .NET Core 3.0 SDK (バージョン 3.0.100 以上) をインストールする
- Visual Studio 2019 (バージョン 16.3 以上) をインストールする
Blazor Server アプリでローカル チャット ルームを作成する
Visual Studio 2019 バージョン 16.2.0 以降では、Azure SignalR Service が Web アプリケーションの発行プロセスに組み込まれているため、Web アプリと SignalR Service との間の依存関係の管理がはるかに容易になっています。 以下においてコードを変更することなく同時に作業できます。
- ローカル開発環境内のローカル SignalR インスタンス内。
- Azure App Service 用の Azure SignalR Service 内。
Blazor チャット アプリを作成します。
Visual Studio で、 [新しいプロジェクトの作成] を選択します。
[Blazor アプリ] を選択します。
アプリケーションに名前を付け、フォルダーを選択します。
[Blazor Server App] (Blazor Server アプリ) テンプレートを選択します。
Note
Visual Studio でターゲット フレームワークが正しく認識されるよう、あらかじめ .NET Core SDK 3.0 以上をインストールしておいてください。
.NET CLI で
dotnet new
コマンドを実行してプロジェクトを作成することもできます。dotnet new blazorserver -o BlazorChat
BlazorChatSampleHub.cs
という名前の新しい C# ファイルを追加し、チャット アプリのHub
クラスから派生した新しいクラスBlazorChatSampleHub
を作成します。 ハブの作成の詳細については、「ハブの作成と使用」を参照してください。using System; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; namespace BlazorChat { public class BlazorChatSampleHub : Hub { public const string HubUrl = "/chat"; public async Task Broadcast(string username, string message) { await Clients.All.SendAsync("Broadcast", username, message); } public override Task OnConnectedAsync() { Console.WriteLine($"{Context.ConnectionId} connected"); return base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception e) { Console.WriteLine($"Disconnected {e?.Message} {Context.ConnectionId}"); await base.OnDisconnectedAsync(e); } } }
Startup.Configure()
メソッドにハブのエンドポイントを追加します。app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); endpoints.MapHub<BlazorChatSampleHub>(BlazorChatSampleHub.HubUrl); });
SignalR クライアントを使用するための
Microsoft.AspNetCore.SignalR.Client
パッケージをインストールします。dotnet add package Microsoft.AspNetCore.SignalR.Client --version 3.1.7
SignalR クライアントを実装するために、
Pages
フォルダーの下にChatRoom.razor
という名前の新しい Razor コンポーネントを作成します。 ChatRoom.razor ファイルを使用するか、以下の手順を実行します。@page
ディレクティブと using ステートメントを追加します。@inject
ディレクティブを使用して、NavigationManager
サービスを挿入します。@page "/chatroom" @inject NavigationManager navigationManager @using Microsoft.AspNetCore.SignalR.Client;
メッセージを送受信するために、
@code
セクションで、次のメンバーを新しい SignalR クライアントに追加します。@code { // flag to indicate chat status private bool _isChatting = false; // name of the user who will be chatting private string _username; // on-screen message private string _message; // new message input private string _newMessage; // list of messages in chat private List<Message> _messages = new List<Message>(); private string _hubUrl; private HubConnection _hubConnection; public async Task Chat() { // check username is valid if (string.IsNullOrWhiteSpace(_username)) { _message = "Please enter a name"; return; }; try { // Start chatting and force refresh UI. _isChatting = true; await Task.Delay(1); // remove old messages if any _messages.Clear(); // Create the chat client string baseUrl = navigationManager.BaseUri; _hubUrl = baseUrl.TrimEnd('/') + BlazorChatSampleHub.HubUrl; _hubConnection = new HubConnectionBuilder() .WithUrl(_hubUrl) .Build(); _hubConnection.On<string, string>("Broadcast", BroadcastMessage); await _hubConnection.StartAsync(); await SendAsync($"[Notice] {_username} joined chat room."); } catch (Exception e) { _message = $"ERROR: Failed to start chat client: {e.Message}"; _isChatting = false; } } private void BroadcastMessage(string name, string message) { bool isMine = name.Equals(_username, StringComparison.OrdinalIgnoreCase); _messages.Add(new Message(name, message, isMine)); // Inform blazor the UI needs updating InvokeAsync(StateHasChanged); } private async Task DisconnectAsync() { if (_isChatting) { await SendAsync($"[Notice] {_username} left chat room."); await _hubConnection.StopAsync(); await _hubConnection.DisposeAsync(); _hubConnection = null; _isChatting = false; } } private async Task SendAsync(string message) { if (_isChatting && !string.IsNullOrWhiteSpace(message)) { await _hubConnection.SendAsync("Broadcast", _username, message); _newMessage = string.Empty; } } private class Message { public Message(string username, string body, bool mine) { Username = username; Body = body; Mine = mine; } public string Username { get; set; } public string Body { get; set; } public bool Mine { get; set; } public bool IsNotice => Body.StartsWith("[Notice]"); public string CSS => Mine ? "sent" : "received"; } }
SignalR クライアントと対話するために、
@code
セクションの前に UI マークアップを追加します。<h1>Blazor SignalR Chat Sample</h1> <hr /> @if (!_isChatting) { <p> Enter your name to start chatting: </p> <input type="text" maxlength="32" @bind="@_username" /> <button type="button" @onclick="@Chat"><span class="oi oi-chat" aria-hidden="true"></span> Chat!</button> // Error messages @if (_message != null) { <div class="invalid-feedback">@_message</div> <small id="emailHelp" class="form-text text-muted">@_message</small> } } else { // banner to show current user <div class="alert alert-secondary mt-4" role="alert"> <span class="oi oi-person mr-2" aria-hidden="true"></span> <span>You are connected as <b>@_username</b></span> <button class="btn btn-sm btn-warning ml-md-auto" @onclick="@DisconnectAsync">Disconnect</button> </div> // display messages <div id="scrollbox"> @foreach (var item in _messages) { @if (item.IsNotice) { <div class="alert alert-info">@item.Body</div> } else { <div class="@item.CSS"> <div class="user">@item.Username</div> <div class="msg">@item.Body</div> </div> } } <hr /> <textarea class="input-lg" placeholder="enter your comment" @bind="@_newMessage"></textarea> <button class="btn btn-default" @onclick="@(() => SendAsync(_newMessage))">Send</button> </div> }
NavMenu.razor
コンポーネントを更新して、NavMenuCssClass
の下のチャット ルームにリンクする新しいNavLink
コンポーネントを挿入します。<li class="nav-item px-3"> <NavLink class="nav-link" href="chatroom"> <span class="oi oi-chat" aria-hidden="true"></span> Chat room </NavLink> </li>
site.css
ファイルにいくつかの CSS クラスを追加して、チャット ページの UI 要素のスタイルを設定します。/* improved for chat text box */ textarea { border: 1px dashed #888; border-radius: 5px; width: 80%; overflow: auto; background: #f7f7f7 } /* improved for speech bubbles */ .received, .sent { position: relative; font-family: arial; font-size: 1.1em; border-radius: 10px; padding: 20px; margin-bottom: 20px; } .received:after, .sent:after { content: ''; border: 20px solid transparent; position: absolute; margin-top: -30px; } .sent { background: #03a9f4; color: #fff; margin-left: 10%; top: 50%; text-align: right; } .received { background: #4CAF50; color: #fff; margin-left: 10px; margin-right: 10%; } .sent:after { border-left-color: #03a9f4; border-right: 0; right: -20px; } .received:after { border-right-color: #4CAF50; border-left: 0; left: -20px; } /* div within bubble for name */ .user { font-size: 0.8em; font-weight: bold; color: #000; } .msg { /*display: inline;*/ }
アプリを実行するためには、F5 キーを押します。 これで、チャットを開始できます。
Azure に発行する
Azure App Service に Blazor アプリをデプロイする場合は、Azure SignalR Service を使用することをお勧めします。 Azure SignalR Service を使うと、Blazor Server アプリを多数の同時 SignalR 接続にスケールアップできます。 さらに、SignalR Service のグローバル リーチとハイパフォーマンスのデータセンターは、地理的条件による待ち時間の短縮に大きく役立ちます。
重要
Blazor Server アプリでは、UI の状態はサーバー側で保持されます。つまり、状態を保持するには、スティッキー サーバー セッションが必要です。 アプリ サーバーが 1 台の場合は、設計によってスティッキー セッションが保証されます。 しかし、複数のアプリ サーバーが使用されている場合は、クライアント ネゴシエーションと接続は異なるサーバーにリダイレクトされる可能性があり、これによって Blazor アプリの UI 状態管理に一貫性がなくなる可能性があります。 そのため、次に示すように appsettings.json 内でスティッキー サーバー セッションを有効にすることが推奨されます。
"Azure:SignalR:ServerStickyMode": "Required"
プロジェクトを右クリックし、 [発行] を選択します。 次の設定を使用します。
- ターゲット: Azure
- 特定のターゲット: すべての種類の Azure App Service がサポートされています。
- App Service: App Service インスタンスを作成または選択します。
Azure SignalR Service の依存関係を追加します。
発行プロファイルの作成後、 [サービスの依存関係] に Azure SignalR Service を追加することを提案するメッセージが表示されます。 このペインで [構成] を選択して、既存の Azure SignalR Service を選択するか、新しく作成してください。
Azure SignalR Service が Azure 上にあればアプリがそれに自動的に切り替わるよう、サービス依存関係によって以下のアクティビティが実行されます。
- Azure SignalR Service を使用するように
HostingStartupAssembly
を更新する。 - Azure SignalR Service NuGet パッケージの参照を追加する。
- プロファイル プロパティを更新して、依存関係の設定を保存する。
- 選択内容に従ってシークレット ストアを構成する。
- Azure SignalR Service がアプリのターゲットとなるように appsettings.json に構成を追加する。
- Azure SignalR Service を使用するように
アプリの発行
これで、アプリを発行する準備が整いました。 発行プロセスが完了すると、ブラウザー内でアプリが自動的に起動されます。
Note
Azure App Service のデプロイが開始されるまでの待ち時間が原因で、アプリの起動に時間がかかる場合があります。 (通常は F12 キーを押して) ブラウザーのデバッガー ツールを使用すると、トラフィックが Azure SignalR Service にリダイレクトされていることを確認できます。
ローカル開発用に Azure SignalR Service を有効にする
次のコマンドを使用して、Azure SignalR SDK への参照を追加します。
dotnet add package Microsoft.Azure.SignalR
次の例に示すように
Startup.ConfigureServices()
内にAddAzureSignalR()
の呼び出しを追加します。public void ConfigureServices(IServiceCollection services) { ... services.AddSignalR().AddAzureSignalR(); ... }
appsettings.json 内で、またはシークレット マネージャー ツールを使用して、Azure SignalR Service の接続文字列を構成します。
Note
手順 2 の代わりに、SignalR SDK を使用するようにホスティング スタートアップ アセンブリを構成することができます。
Azure SignalR Service を有効にするための構成を appsettings.json に追加します。
"Azure": { "SignalR": { "Enabled": true, "ConnectionString": <your-connection-string> } }
Azure SignalR SDK を使用するようにホスティング スタートアップ アセンブリを構成します。 launchSettings.json を編集して、
environmentVariables
内に次の例のような構成を追加します。"environmentVariables": { ..., "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.Azure.SignalR" }
リソースをクリーンアップする
このチュートリアルで作成したリソースをクリーンアップするには、Azure portal を使用してリソース グループを削除します。
その他のリソース
次のステップ
このチュートリアルでは、以下の内容を学習しました。
- Blazor Server アプリ テンプレートを使用して簡単なチャット ルームを作成する。
- Razor コンポーネントを操作する。
- Razor コンポーネントのイベント処理とデータ バインディングを使用する。
- Visual Studio で Azure App Service にすばやくデプロイする。
- ローカルの SignalR から Azure SignalR Service に移行する。
高可用性についての詳細を参照してください。