Краткое руководство. Создание комнаты чата с помощью Служба SignalR

Azure SignalR — это служба Azure, которая помогает разработчикам без труда создавать веб-приложения с функциями в режиме реального времени.

В этой статье показано, как приступить к работе со службой Azure SignalR. В этом кратком руководстве вы создадите приложение чата с помощью веб-приложения ASP.NET Core. Это приложение установит соединение с ресурсом службы Azure SignalR для включения обновлений содержимого в режиме реального времени. Вы разместите веб-приложение локально и подключите к нему несколько браузерных клиентов. Каждый клиент сможет принудительно отправлять обновления содержимого остальным клиентам.

Вы можете использовать любой редактор кода для выполнения шагов в этом кратком руководстве. Одним из вариантов является Visual Studio Code, который доступен на платформах Windows, macOS и Linux.

Код для этого руководства доступен для скачивания в репозитории примеров AzureSignalR на GitHub. Ресурсы Azure, используемые в этом кратком руководстве, можно создать, следуя инструкции по созданию скрипта Служба SignalR.

Если у вас еще нет подписки Azure, создайте бесплатную учетную запись Azure, прежде чем начать работу.

Готовы?

Необходимые компоненты

Возникли проблемы? См. руководство по устранению неполадок или сообщите о проблеме нам.

Создание ресурса Azure SignalR

В этом разделе описано, как создать базовый экземпляр Azure SignalR, используемый для приложения. Следующие действия используют портал Azure для создания нового экземпляра, но также можно использовать Azure CLI. Дополнительные сведения см. в статье az signalr create command in the Служба Azure SignalR CLI Reference.

  1. Войдите на портал Azure.
  2. Щелкните + Создать ресурс в левом верхнем углу страницы.
  3. На странице "Создание ресурса" в текстовом поле служба и Marketplace введите сигнализатор и выберите Служба SignalR из списка.
  4. На странице Служба SignalR нажмите кнопку "Создать".
  5. На вкладке "Основные сведения" введите необходимые сведения для нового экземпляра Служба SignalR. Введите следующие значения:
Поле Рекомендуемое значение Description
Подписка Выберите свою подписку Выберите подписку, которую вы хотите использовать для создания нового экземпляра Служба SignalR.
Группа ресурсов Создайте группу ресурсов с именем SignalRTestResources Выберите или создайте группу ресурсов для ресурса SignalR. Для этого руководства рекомендуется создать новую группу ресурсов вместо использования существующей группы ресурсов. Чтобы освободить ресурсы после завершения работы с руководством, удалите группу ресурсов.

При удалении группы ресурсов также удаляются все ресурсы, принадлежащие группе. Это действие невозможно отменить. Перед удалением группы ресурсов убедитесь, что она не содержит ресурсов, которые вы хотите сохранить.

Дополнительные сведения см. в статье Управление ресурсами Azure через портал.
Имя ресурса testsignalr Введите уникальное имя для ресурса SignalR. Если testsignalr уже взят в регионе, добавьте цифру или символ, пока имя не будет уникальным.

Имя должно быть строкой длиной от 1 до 63 символов и содержать только цифры, буквы и символ -. Имя не может начинаться или заканчиваться символом дефиса, а последовательные символы дефиса недопустимы.
Регион Выберите регион Выберите соответствующий регион для нового экземпляра Служба SignalR.

Служба Azure SignalR сейчас недоступен во всех регионах. Дополнительные сведения см. в разделе Служба Azure SignalR доступности региона
Ценовая категория Выберите "Изменить", а затем выберите "Только для разработки и тестирования". Выберите "Выбрать ", чтобы подтвердить выбор ценовой категории. Служба Azure SignalR имеет три ценовых категории: "Бесплатный", "Стандартный" и "Премиум". Учебники используют уровень "Бесплатный " , если не указано иное в предварительных требованиях.

Дополнительные сведения о различиях функций между уровнями и ценами см. в Служба Azure SignalR ценах
Режим службы Выбор соответствующего режима службы Используйте Значение по умолчанию при размещении логики Концентратора SignalR в веб-приложениях и использовании службы SignalR в качестве прокси-сервера. Используйте бессерверные технологии, такие как Функции Azure для размещения логики концентратора SignalR.

Классический режим предназначен только для обратной совместимости и не рекомендуется использовать.

Дополнительные сведения см. в разделе "Режим службы" в Служба Azure SignalR.

Вам не нужно изменять параметры на вкладках "Сеть и теги " для учебников SignalR.

  1. Нажмите кнопку "Просмотр и создание " в нижней части вкладки "Основные сведения".
  2. На вкладке "Просмотр и создание" просмотрите значения и нажмите кнопку "Создать". Для завершения развертывания потребуется несколько минут.
  3. По завершении развертывания нажмите кнопку "Перейти к ресурсу ".
  4. На странице ресурсов SignalR выберите "Ключи" в меню слева в разделе Параметры.
  5. Скопируйте строку Подключение ion для первичного ключа. Это строка подключения вам потребуется, чтобы настроить приложение позже в этом руководстве.

Создание веб-приложения ASP.NET Core

В этом разделе вы примените .NET Core CLI для создания проекта веб-приложения MVC для ASP.NET Core. Преимущество использования .NET Core CLI по сравнению с Visual Studio заключается в том, что он доступен на платформах Windows, macOS и Linux.

  1. Создайте каталог для своего проекта. В этом кратком руководстве используется папка chattest .

  2. В новой папке выполните следующую команду, чтобы создать проект:

    dotnet new web
    

Добавление менеджера секретных ключей в проект

В этом разделе вы добавите в проект инструмент Диспетчер секретов. Инструмент "Диспетчер секретов" хранит конфиденциальные данные для разработки вне вашего дерева проектов. Такой подход помогает предотвратить случайную компрометацию секретов приложений в исходном коде.

  1. В папке выполните UserSecretsId следующую команду:

    dotnet user-secrets init
    
  2. Добавьте секрет с именем Azure: SignalR: ConnectionString в диспетчер секретов.

    Этот секрет будет содержать строку подключения для получения доступа к ресурсам службы SignalR. Azure:SignalR:ConnectionString — ключ конфигурации по умолчанию, который SignalR ищет для установки подключения. Замените значение, включенное в приведенную ниже команду, строкой подключения к реальному ресурсу службы SignalR.

    Эту команду необходимо выполнить в том же каталоге csproj , что и файл.

    dotnet user-secrets set Azure:SignalR:ConnectionString "<Your connection string>"
    

    Диспетчер секретов будет использоваться только для тестирования веб-приложения при локальном размещении. В следующем руководстве вы развернете веб-приложение чата в Azure. После развертывания веб-приложения в Azure вы будете использовать параметр приложения, а не строку подключения из диспетчера секретов.

    Доступ к этому секрету осуществляется через API конфигурации. Двоеточие (:) используется в имени конфигурации при работе с API конфигурации на всех поддерживаемых платформах. Дополнительные сведения см. в статье Конфигурация в .NET Core.

Добавление веб-приложения Azure SignalR

  1. Добавьте ссылку на пакет NuGet Microsoft.Azure.SignalR, выполнив следующую команду:

    dotnet add package Microsoft.Azure.SignalR
    
  2. Откройте Program.cs и обновите код следующим образом, он вызывает AddSignalR() методы и AddAzureSignalR() методы для использования Служба Azure SignalR:

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddSignalR().AddAzureSignalR();
    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.UseRouting();
    app.UseStaticFiles();
    app.MapHub<ChatSampleHub>("/chat");
    app.Run();
    

    Не передавая параметр, AddAzureSignalR() он использует ключ конфигурации по умолчанию для Служба SignalR ресурса строка подключения. По умолчанию используется ключ конфигурации Azure:SignalR:ConnectionString. Он также использует ChatSampleHub , который мы создадим в следующем разделе.

Добавление класса концентратора

В SignalR концентратор — это основной компонент, предоставляющий набор методов, которые могут вызываться клиентом. В этом разделе необходимо определить класс концентратора с двумя методами:

  • BroadcastMessage: этот метод осуществляет широковещательную рассылку сообщений всем клиентам.
  • Echo: этот метод отправляет сообщение обратно в вызывающий объект.

Оба метода используют интерфейс Clients, предоставляемый пакетом SDK для SignalR и ASP.NET Core. Этот интерфейс предоставляет доступ ко всем подключенным клиентам, позволяя отправлять в них содержимое.

  1. В каталоге проекта добавьте новую папку с именем Hub. Добавьте новый файл кода концентратора с именем ChatSampleHub.cs в новую папку.

  2. Добавьте следующий код в ChatSampleHub.cs , чтобы определить класс концентратора и сохранить файл.

    using Microsoft.AspNetCore.SignalR;
    
    public class ChatSampleHub : Hub
    {
        public Task BroadcastMessage(string name, string message) =>
            Clients.All.SendAsync("broadcastMessage", name, message);
    
        public Task Echo(string name, string message) =>
            Clients.Client(Context.ConnectionId)
                    .SendAsync("echo", name, $"{message} (echo from server)");
    }
    

Добавление клиентского интерфейса для веб-приложения

Пользовательский интерфейс клиента для этого приложения чата представлен в коде HTML и JavaScript в файле с именем index.html в каталоге wwwroot.

Скопируйте файл css/site.css из папки wwwroot в репозитории примеров. Замените файл css/site.css проекта скопированным файлом.

Создайте файл в каталоге wwwroot с именем index.html, скопируйте и вставьте следующий HTML-код в только что созданный файл.

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
  <meta name="viewport" content="width=device-width">
  <meta http-equiv="Pragma" content="no-cache" />
  <meta http-equiv="Expires" content="0" />
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet" />
  <link href="css/site.css" rel="stylesheet" />
  <title>Azure SignalR Group Chat</title>
</head>
<body>
  <h2 class="text-center" style="margin-top: 0; padding-top: 30px; padding-bottom: 30px;">Azure SignalR Group Chat</h2>
  <div class="container" style="height: calc(100% - 110px);">
    <div id="messages" style="background-color: whitesmoke; "></div>
    <div style="width: 100%; border-left-style: ridge; border-right-style: ridge;">
      <textarea id="message" style="width: 100%; padding: 5px 10px; border-style: hidden;"
        placeholder="Type message and press Enter to send..."></textarea>
    </div>
    <div style="overflow: auto; border-style: ridge; border-top-style: hidden;">
      <button class="btn-warning pull-right" id="echo">Echo</button>
      <button class="btn-success pull-right" id="sendmessage">Send</button>
    </div>
  </div>
  <div class="modal alert alert-danger fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <div>Connection Error...</div>
          <div><strong style="font-size: 1.5em;">Hit Refresh/F5</strong> to rejoin. ;)</div>
        </div>
      </div>
    </div>
  </div>

  <!--Reference the SignalR library. -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>

  <!--Add script to update the page and send messages.-->
  <script type="text/javascript">
    document.addEventListener("DOMContentLoaded", function () {
      function getUserName() {
        function generateRandomName() {
          return Math.random().toString(36).substring(2, 10);
        }

        // Get the user name and store it to prepend to messages.
        var username = generateRandomName();
        var promptMessage = "Enter your name:";
        do {
          username = prompt(promptMessage, username);
          if (!username || username.startsWith("_") || username.indexOf("<") > -1 || username.indexOf(">") > -1) {
            username = "";
            promptMessage = "Invalid input. Enter your name:";
          }
        } while (!username)
        return username;
      }

      username = getUserName();
      // Set initial focus to message input box.
      var messageInput = document.getElementById("message");
      messageInput.focus();

      function createMessageEntry(encodedName, encodedMsg) {
        var entry = document.createElement("div");
        entry.classList.add("message-entry");
        if (encodedName === "_SYSTEM_") {
          entry.innerHTML = encodedMsg;
          entry.classList.add("text-center");
          entry.classList.add("system-message");
        } else if (encodedName === "_BROADCAST_") {
          entry.classList.add("text-center");
          entry.innerHTML = `<div class="text-center broadcast-message">${encodedMsg}</div>`;
        } else if (encodedName === username) {
          entry.innerHTML = `<div class="message-avatar pull-right">${encodedName}</div>` +
            `<div class="message-content pull-right">${encodedMsg}<div>`;
        } else {
          entry.innerHTML = `<div class="message-avatar pull-left">${encodedName}</div>` +
            `<div class="message-content pull-left">${encodedMsg}<div>`;
        }
        return entry;
      }

      function appendMessage(encodedName, encodedMsg) {
        var messageEntry = createMessageEntry(encodedName, encodedMsg);
        var messageBox = document.getElementById("messages");
        messageBox.appendChild(messageEntry);
        messageBox.scrollTop = messageBox.scrollHeight;
      }

      function bindConnectionMessage(connection) {
        var messageCallback = function (name, message) {
          if (!message) return;
          // Html encode display name and message.
          var encodedName = name;
          var encodedMsg = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
          appendMessage(encodedName, encodedMsg);
        };
        // Create a function that the hub can call to broadcast messages.
        connection.on("broadcastMessage", messageCallback);
        connection.on("echo", messageCallback);
        connection.onclose(onConnectionError);
      }

      function onConnected(connection) {
        console.log("connection started");
        connection.send("broadcastMessage", "_SYSTEM_", username + " JOINED");
        document.getElementById("sendmessage").addEventListener("click", function (event) {
          // Call the broadcastMessage method on the hub.
          if (messageInput.value) {
            connection.send("broadcastMessage", username, messageInput.value)
              .catch((e) => appendMessage("_BROADCAST_", e.message));
          }

          // Clear text box and reset focus for next comment.
          messageInput.value = "";
          messageInput.focus();
          event.preventDefault();
        });
        document.getElementById("message").addEventListener("keypress", function (event) {
          if (event.keyCode === 13) {
            event.preventDefault();
            document.getElementById("sendmessage").click();
            return false;
          }
        });
        document.getElementById("echo").addEventListener("click", function (event) {
          // Call the echo method on the hub.
          connection.send("echo", username, messageInput.value);

          // Clear text box and reset focus for next comment.
          messageInput.value = "";
          messageInput.focus();
          event.preventDefault();
        });
      }

      function onConnectionError(error) {
        if (error && error.message) {
          console.error(error.message);
        }
        var modal = document.getElementById("myModal");
        modal.classList.add("in");
        modal.style = "display: block;";
      }

      var connection = new signalR.HubConnectionBuilder()
        .withUrl("/chat")
        .build();
      bindConnectionMessage(connection);
      connection.start()
        .then(function () {
          onConnected(connection);
        })
        .catch(function (error) {
          console.error(error.message);
        });
    });
  </script>
</body>
</html>

Код в файле index.html вызывает HubConnectionBuilder.build() для установки HTTP-подключения к ресурсу Azure SignalR.

Если подключение выполнено успешно, это соединение передается в bindConnectionMessage, при этом добавляются обработчики событий для входящей передачи содержимого в клиент.

HubConnection.start() запускает обмен данными с концентратором. Затем onConnected() добавляет обработчики событий кнопки. Эти обработчики используют это соединение, чтобы позволить клиенту передавать обновления содержимого для всех подключенных клиентов.

Создание и запуск приложения локально

  1. Выполните следующую команду, чтобы запустить веб-приложение локально:

    dotnet run
    

    Приложение будет размещено локально с выходными данными, содержащими URL-адрес localhost, например следующим образом:

    Building...
    info: Microsoft.Hosting.Lifetime[14]
          Now listening on: http://localhost:5000
    info: Microsoft.Hosting.Lifetime[0]
          Application started. Press Ctrl+C to shut down.
    info: Microsoft.Hosting.Lifetime[0]
          Hosting environment: Development
    
  2. Откройте два окна браузера. В каждом браузере перейдите по URL-адресу localhost, показанному в окне вывода, например http://localhost:5000/ , как показано в приведенном выше окне вывода. Вам будет предложено ввести имя. Введите имя клиента для обоих клиентов и протестируйте передачу сообщений между клиентами с помощью кнопки Отправить.

    Example of an Azure SignalR group chat

Очистка ресурсов

Для работы со следующим руководством можно сохранить и использовать ресурсы, созданные в этом кратком руководстве.

Если вы не намерены далее работать с примером приложения из этого краткого руководства, удалите созданные для него ресурсы Azure, чтобы избежать ненужных расходов.

Важно!

Удаление группы ресурсов необратимо и распространяется на все ресурсы в группе. Будьте внимательны, чтобы случайно не удалить не ту группу ресурсов или не те ресурсы. Если вы создали ресурсы в этом примере в существующей группе ресурсов, содержащей ресурсы, которые необходимо сохранить, вы можете удалить каждый ресурс по отдельности из колонки вместо удаления группы ресурсов.

Войдите в портал Azure и выберитеГруппы ресурсов.

Введите имя группы ресурсов в текстовое поле Фильтровать по имени. В инструкциях к этому краткому руководству была использована группа ресурсов с именем SignalRTestResources. В группе ресурсов в списке результатов выберите многоточие (...) >Удаление группы ресурсов.

Selections for deleting a resource group

Подтвердите операцию удаления группы ресурсов. Введите имя группы ресурсов, которую необходимо удалить, и нажмите Удалить.

Через некоторое время группа ресурсов и все ее ресурсы будут удалены.

Возникли проблемы? См. руководство по устранению неполадок или сообщите о проблеме нам.

Следующие шаги

В этом кратком руководстве вы создали ресурс Службы Azure SignalR. Затем вы использовали его с веб-приложением ASP.NET Core для отправки обновлений содержимого в режиме реального времени нескольким подключенным клиентам. Чтобы получить дополнительные сведения об использовании Службы Azure SignalR, перейдите к руководству по проверке подлинности.