Поделиться через


Учебник. Создание чат-приложения с помощью службы Azure Web PubSub

В руководстве по публикации и подписке на сообщения вы узнаете, как публиковать и подписывать сообщения с помощью Azure Web PubSub. В этом руководстве описана система событий Azure Web PubSub и ее использование для создания полного веб-приложения с функциями взаимодействия в режиме реального времени.

В этом руководстве описано следующее:

  • Создание экземпляра службы Web PubSub.
  • настроить параметры обработчика событий для Azure Web PubSub;
  • События Hanlde на сервере приложений и создание приложения чата в режиме реального времени

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

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

  • Используйте среду Bash в Azure Cloud Shell. Дополнительные сведения см . в кратком руководстве по Bash в Azure Cloud Shell.

  • Если вы предпочитаете выполнять справочные команды CLI локально, установите Azure CLI. Если вы работаете в Windows или macOS, Azure CLI можно запустить в контейнере Docker. Дополнительные сведения см. в статье Как запустить Azure CLI в контейнере Docker.

    • Если вы используете локальную установку, выполните вход в Azure CLI с помощью команды az login. Чтобы выполнить аутентификацию, следуйте инструкциям в окне терминала. Сведения о других возможностях, доступных при входе, см. в статье Вход с помощью Azure CLI.

    • Установите расширение Azure CLI при первом использовании, когда появится соответствующий запрос. Дополнительные сведения о расширениях см. в статье Использование расширений с Azure CLI.

    • Выполните команду az version, чтобы узнать установленную версию и зависимые библиотеки. Чтобы обновиться до последней версии, выполните команду az upgrade.

  • Для работы с этим руководством требуется Azure CLI версии 2.22.0 или выше. Если вы используете Azure Cloud Shell, последняя версия уже установлена.

Создание экземпляра Azure Web PubSub

Создание или изменение группы ресурсов

Группа ресурсов — это логический контейнер, в котором происходит развертывание ресурсов Azure и управление ими. Используйте команду az group create, чтобы создать группу ресурсов с именем myResourceGroup в расположенииeastus.

az group create --name myResourceGroup --location EastUS

Создание экземпляра Web PubSub

Запустите az extension add to install or upgrade the webpubsub extension to the current version.

az extension add --upgrade --name webpubsub

Используйте команду azure CLI az webpubsub create , чтобы создать web PubSub в созданной группе ресурсов. Следующая команда создает ресурс Free Web PubSub в группе ресурсов myResourceGroup в EastUS:

Внимание

Каждый ресурс Web PubSub должен иметь уникальное имя. В следующих примерах замените <your-unique-resource-name> именем своей службы Web PubSub.

az webpubsub create --name "<your-unique-resource-name>" --resource-group "myResourceGroup" --location "EastUS" --sku Free_F1

В выходных данных команды будут показаны свойства созданного ресурса. Запишите значения двух указанных ниже свойств.

  • Имя ресурса: имя, которое вы ввели для указанного выше параметра --name.
  • hostName: в примере имя узла <your-unique-resource-name>.webpubsub.azure.com/.

На данном этапе любые операции в этом новом ресурсе могут выполняться только с использованием вашей учетной записи Azure.

Получение ConnectionString для будущего использования

Внимание

Строка подключения содержит сведения об авторизации, требуемые для доступа приложения к службе Azure Web PubSub. Ключ доступа в строке подключения аналогичен паролю привилегированного пользователя для службы. В рабочих средах всегда следует тщательно защищать ключи доступа. Для безопасного управления ключами и их замены воспользуйтесь Azure Key Vault. Старайтесь не распространять ключи доступа среди других пользователей, жестко программировать их или где-то сохранять в виде обычного текста в открытом доступе для других пользователей. Меняйте свои ключи постоянно, если предполагаете, что они могут быть подобраны.

Выполните команду Azure CLI az webpubsub key, чтобы получить значение ConnectionString службы. Замените <your-unique-resource-name> заполнитель именем экземпляра Azure Web PubSub.

az webpubsub key show --resource-group myResourceGroup --name <your-unique-resource-name> --query primaryConnectionString --output tsv

Скопируйте строка подключения для последующего использования.

Скопируйте извлекаемый Подключение ionString и задайте его в переменную WebPubSubConnectionStringсреды, которую учебник позже читает. Замените <connection-string> приведенный ниже элементом Подключение ionString, который вы извлекли.

export WebPubSubConnectionString="<connection-string>"
SET WebPubSubConnectionString=<connection-string>

Настройка проекта

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

Создание приложения

В Azure Web PubSub есть две роли: сервер и клиент. Они аналогичны ролям сервера и клиента в веб-приложении. Сервер отвечает за управление клиентами, прослушивание и реагирование на клиентские сообщения. Клиент отвечает за отправку и получение сообщений пользователя с сервера и визуализацию их для конечного пользователя.

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

Мы используем ASP.NET Core 8 для размещения веб-страниц и обработки входящих запросов.

Сначала создадим веб-приложение ASP.NET Core в папке chatapp .

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

    mkdir chatapp
    cd chatapp
    dotnet new web
    
  2. Добавьте app.UseStaticFiles() Program.cs для поддержки размещения статических веб-страниц.

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.UseStaticFiles();
    
    app.Run();
    
  3. Создайте HTML-файл и сохраните его как wwwroot/index.html, мы используем его для пользовательского интерфейса приложения чата позже.

    <html>
      <body>
        <h1>Azure Web PubSub Chat</h1>
      </body>
    </html>
    

Вы можете протестировать сервер, запустив dotnet run --urls http://localhost:8080 и доступ http://localhost:8080/index.html в браузере.

Добавление конечной точки согласования

В руководстве по публикации и подписке подписчик использует строка подключения напрямую. В реальном мире приложение не безопасно совместно использовать строка подключения с любым клиентом, так как строка подключения имеет высокий уровень привилегий для выполнения любой операции со службой. Теперь давайте будем использовать строка подключения сервера и предоставлять конечную negotiate точку для клиента, чтобы получить полный URL-адрес с маркером доступа. Таким образом, сервер может добавить ПО промежуточного слоя проверки подлинности перед конечной negotiate точкой, чтобы предотвратить несанкционированный доступ.

Сначала установите зависимости.

dotnet add package Microsoft.Azure.WebPubSub.AspNetCore

Теперь добавим конечную /negotiate точку для вызова клиента для создания маркера.

using Azure.Core;
using Microsoft.Azure.WebPubSub.AspNetCore;
using Microsoft.Azure.WebPubSub.Common;
using Microsoft.Extensions.Primitives;

// Read connection string from environment
var connectionString = Environment.GetEnvironmentVariable("WebPubSubConnectionString");
if (connectionString == null)
{
    throw new ArgumentNullException(nameof(connectionString));
}

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddWebPubSub(o => o.ServiceEndpoint = new WebPubSubServiceEndpoint(connectionString))
    .AddWebPubSubServiceClient<Sample_ChatApp>();
var app = builder.Build();

app.UseStaticFiles();

// return the Client Access URL with negotiate endpoint
app.MapGet("/negotiate", (WebPubSubServiceClient<Sample_ChatApp> service, HttpContext context) =>
{
    var id = context.Request.Query["id"];
    if (StringValues.IsNullOrEmpty(id))
    {
        context.Response.StatusCode = 400;
        return null;
    }
    return new
    {
        url = service.GetClientAccessUri(userId: id).AbsoluteUri
    };
});
app.Run();

sealed class Sample_ChatApp : WebPubSubHub
{
}

AddWebPubSubServiceClient<THub>() используется для внедрения клиента WebPubSubServiceClient<THub>службы, с помощью которого мы можем использовать в шаге согласования для создания маркера подключения клиента и в методах концентратора для вызова ИНТЕРФЕЙСов REST API службы при активации событий концентратора. Этот код создания маркера аналогичен тому, который мы использовали в учебнике по публикации сообщения и подписке на него, за исключением того, что передается еще один аргумент (userId). Идентификатор пользователя можно использовать для идентификации клиента, так что при получении сообщения вы знаете, откуда оно поступило.

Код считывает строка подключения из переменной WebPubSubConnectionString среды, заданной на предыдущем шаге.

Повторное выполнение сервера с помощью dotnet run --urls http://localhost:8080.

Вы можете протестировать этот API, выполнив доступ http://localhost:8080/negotiate?id=user1 и предоставив полный URL-адрес веб-pubSub Azure с маркером доступа.

Обработка событий

В Azure Web PubSub, когда на стороне клиента происходят определенные действия (например, клиент подключается, подключен, отключен или клиент отправляет сообщения), служба отправляет уведомления на сервер, чтобы реагировать на эти события.

События доставляются на сервер в форме веб-перехватчика. Веб-перехватчик предоставляется сервером приложений и регистрируется на стороне службы Azure Web PubSub. Служба вызывает веб-перехватчики каждый раз, когда происходит событие.

Описание данных событий в Azure Web PubSub соответствует спецификации CloudEvents.

Ниже мы обрабатываем connected системные события, когда клиент подключен и обрабатывает message события пользователя, когда клиент отправляет сообщения для создания приложения чата.

Пакет SDK Web PubSub для AspNetCore Microsoft.Azure.WebPubSub.AspNetCore , установленный на предыдущем шаге, также может помочь проанализировать и обработать запросы CloudEvents.

Сначала добавьте обработчики событий.app.Run() Укажите путь к конечной точке для событий, допустим /eventhandler.

app.MapWebPubSubHub<Sample_ChatApp>("/eventhandler/{*path}");
app.Run();

Теперь внутри класса Sample_ChatApp , созданного на предыдущем шаге, добавьте конструктор для работы со WebPubSubServiceClient<Sample_ChatApp> службой Web PubSub. И OnConnectedAsync() реагировать на события при connected активации события для OnMessageReceivedAsync() обработки сообщений от клиента.

sealed class Sample_ChatApp : WebPubSubHub
{
    private readonly WebPubSubServiceClient<Sample_ChatApp> _serviceClient;

    public Sample_ChatApp(WebPubSubServiceClient<Sample_ChatApp> serviceClient)
    {
        _serviceClient = serviceClient;
    }

    public override async Task OnConnectedAsync(ConnectedEventRequest request)
    {
        Console.WriteLine($"[SYSTEM] {request.ConnectionContext.UserId} joined.");
    }

    public override async ValueTask<UserEventResponse> OnMessageReceivedAsync(UserEventRequest request, CancellationToken cancellationToken)
    {
        await _serviceClient.SendToAllAsync(RequestContent.Create(
        new
        {
            from = request.ConnectionContext.UserId,
            message = request.Data.ToString()
        }),
        ContentType.ApplicationJson);

        return new UserEventResponse();
    }
}

В приведенном выше коде мы используем клиент службы для трансляции сообщения уведомления в формате JSON, с которым присоединяется SendToAllAsyncвсе.

Обновление веб-страницы

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

<html>
  <body>
    <h1>Azure Web PubSub Chat</h1>
    <input id="message" placeholder="Type to chat...">
    <div id="messages"></div>
    <script>
      (async function () {
        let id = prompt('Please input your user name');
        let res = await fetch(`/negotiate?id=${id}`);
        let data = await res.json();
        let ws = new WebSocket(data.url);
        ws.onopen = () => console.log('connected');

        let messages = document.querySelector('#messages');
        
        ws.onmessage = event => {
          let m = document.createElement('p');
          let data = JSON.parse(event.data);
          m.innerText = `[${data.type || ''}${data.from || ''}] ${data.message}`;
          messages.appendChild(m);
        };

        let message = document.querySelector('#message');
        message.addEventListener('keypress', e => {
          if (e.charCode !== 13) return;
          ws.send(message.value);
          message.value = '';
        });
      })();
    </script>
  </body>

</html>

В приведенном выше коде мы подключаем собственный API WebSocket в браузере и используем WebSocket.send() для отправки сообщений и WebSocket.onmessage прослушивания полученных сообщений.

Вы также можете использовать клиентские пакеты SDK для подключения к службе, что позволяет выполнять автоматическое повторное подключение, обработку ошибок и многое другое.

Теперь для работы чата осталось один шаг. Давайте настроим события, о которых мы заботимся, и где отправлять события в службу Web PubSub.

Настройка обработчика событий

Мы задали обработчик событий в службе Web PubSub, чтобы сообщить службе, куда отправлять события.

Когда веб-сервер выполняется локально, как служба Web PubSub вызывает localhost, если у нее нет конечной точки доступа к Интернету? Обычно существует два способа. Одним из них является предоставление локального узла общедоступным пользователям с помощью какого-то общего средства туннелирования, а другой — использовать awps-туннель для туннелирования трафика из службы Web PubSub через средство на локальный сервер.

В этом разделе мы используем Azure CLI для задания обработчиков событий и использования awps-tunnel для маршрутизации трафика в localhost.

Настройка параметров концентратора

Мы задали шаблон URL-адреса для использования tunnel схемы, чтобы Web PubSub направляет сообщения через awps-tunnelтуннельное подключение. Обработчики событий можно задать на портале или интерфейсе командной строки, как описано в этой статье, здесь мы зададим его через CLI. Так как мы прослушиваем события в пути /eventhandler в качестве предыдущих наборов шагов, мы зададим шаблон tunnel:///eventhandlerURL-адреса.

Используйте команду создания azure CLI az webpubsub hub, чтобы создать параметры обработчика событий для концентратораSample_ChatApp.

Внимание

Замените <your-unique-resource-name> на имя ресурса Web PubSub, созданного на предыдущих шагах.

az webpubsub hub create -n "<your-unique-resource-name>" -g "myResourceGroup" --hub-name "Sample_ChatApp" --event-handler url-template="tunnel:///eventhandler" user-event-pattern="*" system-event="connected"

Локальное выполнение awps-туннеля

Скачивание и установка awps-tunnel

Средство работает на Node.js версии 16 или более поздней.

npm install -g @azure/web-pubsub-tunnel-tool

Использование службы строка подключения и запуск

export WebPubSubConnectionString="<your connection string>"
awps-tunnel run --hub Sample_ChatApp --upstream http://localhost:8080

Запуск веб-сервера

Теперь все задано. Давайте запустите веб-сервер и сыграем с приложением чата в действии.

Теперь запустите сервер с помощью dotnet run --urls http://localhost:8080.

Полный пример кода для этого учебника можно найти здесь.

Открыть http://localhost:8080/index.html. Вы можете ввести имя пользователя и начать чат.

Отложенная проверка подлинности с connect обработчиком событий

В предыдущих разделах показано, как использовать конечную точку согласования для возврата URL-адреса службы Web PubSub и маркера доступа JWT для клиентов для подключения к службе Web PubSub. В некоторых случаях, например, пограничные устройства с ограниченными ресурсами, клиенты могут предпочесть прямое подключение к ресурсам Web PubSub. В таких случаях обработчик событий можно настроить connect для отложенной проверки подлинности клиентов, назначить идентификатор пользователя клиентам, указать группы, которые клиенты присоединяются после подключения, настроить разрешения клиентов и subprotocol WebSocket в качестве ответа WebSocket клиенту и т. д. Дополнительные сведения см. в спецификации обработчика событий.

Теперь давайте используем connect обработчик событий для достижения аналогичного того, что делает раздел переговоров .

Обновление параметров центра

Сначала давайте обновим параметры концентратора для включения обработчика connect событий, необходимо также разрешить анонимное подключение, чтобы клиенты без маркера доступа JWT могли подключаться к службе.

Используйте команду центра обновления Azure CLI az webpubsub hub, чтобы создать параметры обработчика событий для концентратораSample_ChatApp.

Внимание

Замените <your-unique-resource-name> на имя ресурса Web PubSub, созданного на предыдущих шагах.

az webpubsub hub update -n "<your-unique-resource-name>" -g "myResourceGroup" --hub-name "Sample_ChatApp" --allow-anonymous true --event-handler url-template="tunnel:///eventhandler" user-event-pattern="*" system-event="connected" system-event="connect"

Обновление логики вышестоящий для обработки события подключения

Теперь давайте обновим логику вышестоящий для обработки события подключения. Теперь можно удалить конечную точку переговоров.

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

В классе Sample_ChatAppпереопределите OnConnectAsync() для обработки connect события:

sealed class Sample_ChatApp : WebPubSubHub
{
    private readonly WebPubSubServiceClient<Sample_ChatApp> _serviceClient;

    public Sample_ChatApp(WebPubSubServiceClient<Sample_ChatApp> serviceClient)
    {
        _serviceClient = serviceClient;
    }

    public override ValueTask<ConnectEventResponse> OnConnectAsync(ConnectEventRequest request, CancellationToken cancellationToken)
    {
        if (request.Query.TryGetValue("id", out var id))
        {
            return new ValueTask<ConnectEventResponse>(request.CreateResponse(userId: id.FirstOrDefault(), null, null, null));
        }

        // The SDK catches this exception and returns 401 to the caller
        throw new UnauthorizedAccessException("Request missing id");
    }

    public override async Task OnConnectedAsync(ConnectedEventRequest request)
    {
        Console.WriteLine($"[SYSTEM] {request.ConnectionContext.UserId} joined.");
    }

    public override async ValueTask<UserEventResponse> OnMessageReceivedAsync(UserEventRequest request, CancellationToken cancellationToken)
    {
        await _serviceClient.SendToAllAsync(RequestContent.Create(
        new
        {
            from = request.ConnectionContext.UserId,
            message = request.Data.ToString()
        }),
        ContentType.ApplicationJson);

        return new UserEventResponse();
    }
}

Обновление index.html для прямого подключения

Теперь обновим веб-страницу, чтобы напрямую подключиться к службе Web PubSub. Одно из упоминание заключается в том, что теперь для демонстрации конечной точки службы Web PubSub жестко закодирован в клиентский код, обновите имя <the host name of your service> узла службы в приведенном ниже html значении из собственной службы. Возможно, вам по-прежнему полезно получить значение конечной точки службы Web PubSub с сервера, что обеспечивает большую гибкость и управляемость, к которой подключается клиент.

<html>
  <body>
    <h1>Azure Web PubSub Chat</h1>
    <input id="message" placeholder="Type to chat...">
    <div id="messages"></div>
    <script>
      (async function () {
        // sample host: mock.webpubsub.azure.com
        let hostname = "<the host name of your service>";
        let id = prompt('Please input your user name');
        let ws = new WebSocket(`wss://${hostname}/client/hubs/Sample_ChatApp?id=${id}`);
        ws.onopen = () => console.log('connected');

        let messages = document.querySelector('#messages');
        
        ws.onmessage = event => {
          let m = document.createElement('p');
          let data = JSON.parse(event.data);
          m.innerText = `[${data.type || ''}${data.from || ''}] ${data.message}`;
          messages.appendChild(m);
        };

        let message = document.querySelector('#message');
        message.addEventListener('keypress', e => {
          if (e.charCode !== 13) return;
          ws.send(message.value);
          message.value = '';
        });
      })();
    </script>
  </body>

</html>

Повторное запуск сервера

Теперь повторно запустите сервер и перейдите на веб-страницу после инструкций, описанных ранее. Если вы остановили awps-tunnel, также повторно запустите средство туннелирования.

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

В этом руководстве вы узнаете, как работает система событий в службе Azure Web PubSub.

Чтобы узнать больше об использовании службы, ознакомьтесь с другими учебниками.