共用方式為


教學課程:建置 Blazor Server 聊天應用程式

本教學課程說明如何建置和修改 Blazor Server 應用程式。 您將學習如何:

  • 使用 Blazor Server 應用程式範本建立簡單的聊天室。
  • 運用 Razor 元件。
  • 使用 Razor 元件中的事件處理和資料繫結。
  • 快速部署至 Visual Studio 中的 Azure App Service。
  • 從本機 SignalR 遷移至 Azure SignalR Service。

準備開始了嗎?

必要條件

有問題嗎? 讓我們知道。

在 Blazor Server 應用程式中建置本機聊天室

從 Visual Studio 2019 16.2.0 版開始,Azure SignalR Service 會內建於 Web 應用程式發佈流程中,協助您輕鬆管理 Web 應用程式和 SignalR Service 之間的相依性。 您可以同時在不進行任何程式碼變更的情況下工作:

  • 在本機 SignalR 執行個體,在本機開發環境中。
  • 在 Azure App Service 的 Azure SignalR Service 中。
  1. 建立 Blazor 聊天應用程式:

    1. 在 Visual Studio 中,選擇 [建立新專案]

    2. 選取 [Blazor 應用程式]

    3. 命名應用程式,然後選擇資料夾。

    4. 選取 Blazor Server 應用程式範本。

      注意

      請確定您已安裝 .NET Core SDK 3.0 +,以便 Visual Studio 能夠正確辨識目標 Framework。

      在 [建立新專案] 中,選取 [Blazor 應用程式範本]。

    5. 您也可以在 .NET CLI 中執行 dotnet new 命令來建立專案:

      dotnet new blazorserver -o BlazorChat
      
  2. 新增名為 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);
            }
        }
    }
    
  3. Startup.Configure() 方法內新增中樞的端點。

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
        endpoints.MapHub<BlazorChatSampleHub>(BlazorChatSampleHub.HubUrl);
    });
    
  4. 安裝 Microsoft.AspNetCore.SignalR.Client 套件以使用 SignalR 用戶端。

    dotnet add package Microsoft.AspNetCore.SignalR.Client --version 3.1.7
    
  5. 若要實作 SignalR 用戶端,請在 Pages 資料夾下建立名為 ChatRoom.razor 的新 Razor 元件。 使用 ChatRoom.razor 檔案,或執行下列步驟:

    1. 新增 @page 指示詞和 using 陳述式。 使用 @inject 指示詞插入 NavigationManager 服務。

      @page "/chatroom"
      @inject NavigationManager navigationManager
      @using Microsoft.AspNetCore.SignalR.Client;
      
    2. @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";
          }
      }
      
    3. @code 區段之前新增 UI 標記,以與 SignalR 用戶端互動。

      <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>
      }
      
  6. 更新 NavMenu.razor 元件以插入新的 NavLink 元件,以連結至 NavMenuCssClass 下方的聊天室。

    <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>
    
  7. 將幾個 CSS 類別新增至 site.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;*/
    }
    
  8. F5 以執行應用程式。 現在,您可以開始聊天:

    Bob 與 Alice 之間的聊天以動畫顯示。Alice 說哈囉,Bob 說嗨。

有問題嗎? 讓我們知道。

發佈至 Azure

將 Blazor 應用程式部署至 Azure App Service 時,建議您使用 Azure SignalR Service。 Azure SignalR Service 允許將 Blazor Server 應用程式擴增為大量的並行 SignalR 連線。 此外,SignalR Service 的全球性和高效能資料中心可大幅縮短因地理位置而造成的延遲。

重要

在 Blazor Server 應用程式中,UI 狀態會保存於伺服器端,這表示需要黏性伺服器工作階段才能保留狀態。 如果只有單一應用程式伺服器,則依設計可確保黏性工作階段。 不過,如果有多個使用中的應用程式伺服器,用戶端交涉和連線可能會重新導向至不同的伺服器,這可能會導致 Blazor 應用程式中的 UI 狀態管理不一致。 因此,建議您啟用黏性伺服器工作階段,如下列 appsettings.json 所示:

"Azure:SignalR:ServerStickyMode": "Required"
  1. 以滑鼠右鍵按一下專案,然後移至 [發佈]。 使用下列設定:

    • 目標:Azure
    • 特定目標:支援所有類型的 Azure App Service
    • App Service:建立或選取 App Service 執行個體。

    動畫顯示已選取 Azure 作為目標,然後選取 Azure App Serice 作為特定目標。

  2. 新增 Azure SignalR Service 相依項目。

    建立發行設定檔之後,您會看到在 [服務相依性] 底下新增 Azure SignalR Service 的建議訊息。 選取 [設定] 以建立新的服務,或在面板中選取現有的 Azure SignalR Service。

    在 [發佈] 上,[設定] 的連結已醒目提示。

    服務相依性會執行下列活動,讓您的應用程式在 Azure 上執行下列作業時,自動切換至 Azure SignalR Service:

    • 更新 HostingStartupAssembly 以使用 Azure SignalR Service。
    • 新增 Azure SignalR Service NuGet 套件參考。
    • 更新設定檔屬性以儲存相依性設定。
    • 根據您的選擇設定秘密存放區。
    • appsettings.json 中新增組態,讓您的應用程式以 Azure SignalR Service 為目標。

    在 [變更摘要] 上,已使用核取方塊選取所有相依性。

  3. 發行應用程式。

    現在您的應用程式已準備好發佈。 發佈程序完成後,應用程式會自動在瀏覽器中啟動。

    注意

    由於 Azure App Service 部署啟動延遲,應用程式可能需要一些時間才能啟動。 您可以使用瀏覽器偵錯工具 (通常是按 F12),確認流量已重新導向至 Azure SignalR Service。

    Blazor SignalR 聊天範例有文字方塊可顯示您的名稱,並且有 [聊天!] 按鈕可展開聊天。

有問題嗎? 讓我們知道。

針對本機開發啟用 Azure SignalR Service

  1. 使用下列命令新增對 Azure SignalR SDK 的參考。

    dotnet add package Microsoft.Azure.SignalR
    
  2. Startup.ConfigureServices() 中新增 AddAzureSignalR() 呼叫,如下列範例所示:

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddSignalR().AddAzureSignalR();
        ...
    }
    
  3. appsettings.json 或使用秘密管理員工具設定 Azure SignalR Service 連接字串。

注意

步驟 2 可替換為設定裝載啟動組件以使用 SignalR SDK。

  1. 新增組態以在 appsettings.json 中開啟 Azure SignalR Service:

    "Azure": {
      "SignalR": {
        "Enabled": true,
        "ConnectionString": <your-connection-string> 
      }
    }
    
    
  2. 設定裝載啟動組件以使用 Azure SignalR SDK。 編輯 launchSettings.json,並在 environmentVariables 中新增組態,如下列範例所示:

    "environmentVariables": {
        ...,
       "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.Azure.SignalR"
     }
    
    

有問題嗎? 讓我們知道。

清除資源

若要清除在本教學課程中建立的資源,請使用 Azure 入口網站刪除資源群組。

其他資源

下一步

在本教學課程中,您已了解如何:

  • 使用 Blazor Server 應用程式範本建立簡單的聊天室。
  • 運用 Razor 元件。
  • 使用 Razor 元件中的事件處理和資料繫結。
  • 快速部署至 Visual Studio 中的 Azure App Service。
  • 從本機 SignalR 遷移至 Azure SignalR Service。

深入了解高可用性: