Share via


Tutorial: Criar um aplicativo de chat com o serviço Azure Web PubSub

No tutorial Publicar e assinar mensagem, você aprende as noções básicas de publicação e assinatura de mensagens com o Azure Web PubSub. Neste tutorial, você aprende o sistema de eventos do Azure Web PubSub e o usa para criar um aplicativo Web completo com funcionalidade de comunicação em tempo real.

Neste tutorial, irá aprender a:

  • Criar uma instância de serviço Web PubSub
  • Definir configurações do manipulador de eventos para o Azure Web PubSub
  • Hanlde eventos no servidor de aplicativos e criar um aplicativo de bate-papo em tempo real

Se não tiver uma subscrição do Azure, crie uma conta gratuita do Azure antes de começar.

Pré-requisitos

  • Use o ambiente Bash no Azure Cloud Shell. Para obter mais informações, consulte Guia de início rápido para Bash no Azure Cloud Shell.

  • Se preferir executar comandos de referência da CLI localmente, instale a CLI do Azure. Se estiver a utilizar o Windows ou macOS, considere executar a CLI do Azure num contentor Docker. Para obter mais informações, consulte Como executar a CLI do Azure em um contêiner do Docker.

    • Se estiver a utilizar uma instalação local, inicie sessão no CLI do Azure ao utilizar o comando az login. Para concluir o processo de autenticação, siga os passos apresentados no seu terminal. Para outras opções de entrada, consulte Entrar com a CLI do Azure.

    • Quando solicitado, instale a extensão da CLI do Azure na primeira utilização. Para obter mais informações sobre as extensões, veja Utilizar extensões com o CLI do Azure.

    • Execute o comando az version para localizar a versão e as bibliotecas dependentes instaladas. Para atualizar para a versão mais recente, execute o comando az upgrade.

  • Esta configuração requer a versão 2.22.0 ou superior da CLI do Azure. Se estiver usando o Azure Cloud Shell, a versão mais recente já está instalada.

Criar uma instância do Azure Web PubSub

Criar um grupo de recursos

Um grupo de recursos é um contentor lógico no qual os recursos do Azure são implementados e geridos. Use o comando az group create para criar um grupo de recursos nomeado myResourceGroup no eastus local.

az group create --name myResourceGroup --location EastUS

Criar uma instância do Web PubSub

Execute az extension add para instalar ou atualizar a extensão webpubsub para a versão atual.

az extension add --upgrade --name webpubsub

Use o comando Azure CLI az webpubsub create para criar um Web PubSub no grupo de recursos que você criou. O comando a seguir cria um recurso Free Web PubSub no grupo de recursos myResourceGroup em EastUS:

Importante

Cada recurso Web PubSub deve ter um nome exclusivo. Substitua <your-unique-resource-name> pelo nome do seu Web PubSub nos exemplos a seguir.

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

A saída deste comando mostra as propriedades do recurso recém-criado. Tome nota das duas propriedades listadas abaixo:

  • Nome do recurso: O nome que você forneceu para o --name parâmetro acima.
  • hostName: No exemplo, o nome do host é <your-unique-resource-name>.webpubsub.azure.com/.

Neste ponto, sua conta do Azure é a única autorizada a executar quaisquer operações neste novo recurso.

Obtenha o ConnectionString para uso futuro

Importante

Uma cadeia de conexão inclui as informações de autorização necessárias para seu aplicativo acessar o serviço Azure Web PubSub. A chave de acesso dentro da cadeia de conexão é semelhante a uma senha de root para o seu serviço. Em ambientes de produção, tenha sempre o cuidado de proteger as suas chaves de acesso. Use o Azure Key Vault para gerenciar e girar suas chaves com segurança. Evite distribuir chaves de acesso para outros usuários, codificá-las ou salvá-las em qualquer lugar em texto simples acessível a outras pessoas. Rode as chaves se acreditar que podem ter sido comprometidas.

Use o comando Azure CLI az webpubsub key para obter o ConnectionString do serviço. Substitua o espaço reservado <your-unique-resource-name> pelo nome da sua instância do Azure Web PubSub.

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

Copie a cadeia de conexão para usar mais tarde.

Copie o ConnectionString buscado e defina-o na variável WebPubSubConnectionStringde ambiente , que o tutorial lê posteriormente. Substitua <connection-string>abaixo pelo ConnectionString que você buscou.

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

Configurar o projeto

Pré-requisitos

Criar a aplicação

No Azure Web PubSub, há duas funções, servidor e cliente. Esse conceito é semelhante às funções de servidor e cliente em um aplicativo Web. O servidor é responsável por gerenciar os clientes, ouvir e responder às mensagens do cliente. O cliente é responsável por enviar e receber mensagens do usuário do servidor e visualizá-las para o usuário final.

Neste tutorial, construímos uma aplicação web de chat em tempo real. Em um aplicativo Web real, a responsabilidade do servidor também inclui a autenticação de clientes e o serviço de páginas da Web estáticas para a interface do usuário do aplicativo.

Usamos o ASP.NET Core 8 para hospedar as páginas da Web e lidar com solicitações recebidas.

Primeiro, vamos criar um aplicativo Web ASP.NET Core em uma chatapp pasta.

  1. Crie um novo aplicativo Web.

    mkdir chatapp
    cd chatapp
    dotnet new web
    
  2. Adicione app.UseStaticFiles() Program.cs para suportar a hospedagem de páginas da Web estáticas.

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.UseStaticFiles();
    
    app.Run();
    
  3. Crie um arquivo HTML e salve-o como wwwroot/index.html, nós o usamos para a interface do usuário do aplicativo de bate-papo mais tarde.

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

Você pode testar o servidor executando dotnet run --urls http://localhost:8080 e acessando http://localhost:8080/index.html no navegador.

Adicionar ponto de extremidade de negociação

No tutorial Publicar e assinar mensagem, o assinante consome cadeia de conexão diretamente. Em um aplicativo do mundo real, não é seguro compartilhar a cadeia de conexão com qualquer cliente, porque a cadeia de conexão tem alto privilégio para fazer qualquer operação para o serviço. Agora, vamos fazer com que seu servidor consuma a cadeia de conexão e exponha um ponto de negotiate extremidade para o cliente obter a URL completa com o token de acesso. Dessa forma, o servidor pode adicionar middleware de autenticação antes do ponto de extremidade para impedir o negotiate acesso não autorizado.

Primeiro, instale as dependências.

dotnet add package Microsoft.Azure.WebPubSub.AspNetCore

Agora vamos adicionar um ponto de /negotiate extremidade para o cliente chamar para gerar o token.

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>() é usado para injetar o cliente WebPubSubServiceClient<THub>de serviço , com o qual podemos usar na etapa de negociação para gerar token de conexão do cliente e nos métodos de hub para invocar APIs REST de serviço quando os eventos de hub são acionados. Este código de geração de token é semelhante ao que usamos no tutorial de publicar e assinar mensagem, exceto que passamos mais um argumento (userId) ao gerar o token. O ID de usuário pode ser usado para identificar a identidade do cliente para que, quando você receber uma mensagem, saiba de onde a mensagem está vindo.

O código lê a cadeia de conexão da variável WebPubSubConnectionString de ambiente que definimos na etapa anterior.

Execute novamente o servidor usando dotnet run --urls http://localhost:8080o .

Você pode testar essa API acessando http://localhost:8080/negotiate?id=user1 e ela fornece a URL completa do Azure Web PubSub com um token de acesso.

Processar eventos

No Azure Web PubSub, quando determinadas atividades acontecem no lado do cliente (por exemplo, um cliente está se conectando, conectado, desconectado ou um cliente está enviando mensagens), o serviço envia notificações ao servidor para que ele possa reagir a esses eventos.

Os eventos são entregues ao servidor na forma de Webhook. O Webhook é servido e exposto pelo servidor de aplicativos e registrado no lado do serviço Azure Web PubSub. O serviço invoca os webhooks sempre que um evento acontece.

O Azure Web PubSub segue o CloudEvents para descrever os dados do evento.

Abaixo, lidamos com connected eventos do sistema quando um cliente está conectado e lidamos com message eventos do usuário quando um cliente está enviando mensagens para criar o aplicativo de bate-papo.

O SDK do Web PubSub para AspNetCore Microsoft.Azure.WebPubSub.AspNetCore que instalamos na etapa anterior também pode ajudar a analisar e processar as solicitações do CloudEvents.

Primeiro, adicione manipuladores de eventos antes app.Run()de . Especifique o caminho do ponto de extremidade para os eventos, digamos /eventhandler.

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

Agora, dentro da classe Sample_ChatApp que criamos na etapa anterior, adicione um construtor para trabalhar com WebPubSubServiceClient<Sample_ChatApp> o qual usamos para invocar o serviço Web PubSub. E OnConnectedAsync() para responder quando connected o evento é acionado, OnMessageReceivedAsync() para lidar com mensagens do cliente.

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();
    }
}

No código acima, usamos o cliente de serviço para transmitir uma mensagem de notificação no formato JSON para todos os que estão associados ao SendToAllAsync.

Atualizar a página Web

Agora vamos atualizar index.html para adicionar a lógica para conectar, enviar mensagens e exibir mensagens recebidas na página.

<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>

Você pode ver no código acima que conectamos usar a API WebSocket nativa no navegador e usar WebSocket.send() para enviar mensagens e WebSocket.onmessage ouvir as mensagens recebidas.

Você também pode usar SDKs de cliente para se conectar ao serviço, o que permite que você reconecte automaticamente, tratamento de erros e muito mais.

Agora falta um passo para o bate-papo funcionar. Vamos configurar quais eventos nos interessam e para onde enviá-los no serviço Web PubSub.

Configurar o manipulador de eventos

Definimos o manipulador de eventos no serviço Web PubSub para informar ao serviço para onde enviar os eventos.

Quando o servidor Web é executado localmente, como o serviço Web PubSub invoca o localhost se ele não tiver um ponto de extremidade acessível pela Internet? Geralmente, há dois caminhos. Um é expor localhost ao público usando alguma ferramenta de túnel geral, e o outro é usar awps-tunnel para encapsular o tráfego do serviço Web PubSub através da ferramenta para o seu servidor local.

Nesta seção, usamos a CLI do Azure para definir os manipuladores de eventos e usar awps-tunnel para rotear o tráfego para localhost.

Definir configurações de hub

Definimos o modelo de URL para usar tunnel o esquema para que o Web PubSub roteie mensagens através da awps-tunnelconexão de túnel do . Os manipuladores de eventos podem ser definidos a partir do portal ou da CLI, conforme descrito neste artigo, aqui nós o definimos por meio da CLI. Como ouvimos eventos no caminho /eventhandler como a etapa anterior define, definimos o modelo de url como tunnel:///eventhandler.

Use o comando Azure CLI az webpubsub hub create para criar as configurações do manipulador de eventos para o Sample_ChatApp hub.

Importante

Substitua <your-unique-resource-name> pelo nome do recurso Web PubSub criado a partir das etapas anteriores.

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"

Execute o túnel awps localmente

Baixe e instale o awps-tunnel

A ferramenta é executada em Node.js versão 16 ou superior.

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

Use a cadeia de conexão de serviço e execute

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

Executar o servidor Web

Agora está tudo definido. Vamos executar o servidor web e jogar com o aplicativo de bate-papo em ação.

Agora execute o servidor usando dotnet run --urls http://localhost:8080o .

O exemplo de código completo deste tutorial pode ser encontrado aqui.

Abrir http://localhost:8080/index.html. Você pode inserir seu nome de usuário e começar a conversar.

Lazy Auth com connect manipulador de eventos

Nas seções anteriores, demonstramos como usar o ponto de extremidade de negociação para retornar a URL do serviço Web PubSub e o token de acesso JWT para que os clientes se conectem ao serviço Web PubSub. Em alguns casos, por exemplo, dispositivos de borda que têm recursos limitados, os clientes podem preferir a conexão direta aos recursos do Web PubSub. Nesses casos, você pode configurar connect o manipulador de eventos para autenticação preguiçosa dos clientes, atribuir ID de usuário aos clientes, especificar os grupos aos quais os clientes ingressam depois de se conectarem, configurar as permissões que os clientes têm e o subprotocolo WebSocket como a resposta WebSocket ao cliente, etc. Detalhes Consulte Especificação do manipulador de eventos de conexão.

Agora vamos usar connect o manipulador de eventos para obter o semelhante ao que a seção de negociação faz.

Atualizar configurações do hub

Primeiro, vamos atualizar as configurações do hub para incluir também connect o manipulador de eventos, precisamos também permitir a conexão anônima para que os clientes sem token de acesso JWT possam se conectar ao serviço.

Use o comando Azure CLI az webpubsub hub update para criar as configurações do manipulador de eventos para o Sample_ChatApp hub.

Importante

Substitua <your-unique-resource-name> pelo nome do recurso Web PubSub criado a partir das etapas anteriores.

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"

Atualizar a lógica upstream para manipular o evento connect

Agora vamos atualizar a lógica upstream para manipular o evento connect. Também poderíamos remover o ponto final de negociação agora.

À semelhança do que fazemos na negociação do ponto final como objetivo de demonstração, também lemos id dos parâmetros de consulta. No evento connect, a consulta do cliente original é preservada no corpo do requet do evento connect.

Dentro da classe Sample_ChatApp, substitua OnConnectAsync() para manipular connect o evento:

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();
    }
}

Atualizar index.html para conexão direta

Agora vamos atualizar a página da Web para conexão direta com o serviço Web PubSub. Uma coisa a mencionar é que agora, para fins de demonstração, o ponto de extremidade do serviço Web PubSub é codificado no código do cliente, atualize o nome <the host name of your service> do host do serviço no html abaixo com o valor do seu próprio serviço. Ainda pode ser útil buscar o valor do ponto de extremidade do serviço Web PubSub do seu servidor, pois oferece mais flexibilidade e controlabilidade para onde o cliente se conecta.

<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>

Execute novamente o servidor

Agora execute novamente o servidor e visite a página da Web seguindo as instruções anteriores. Se você parou awps-tunnel, execute novamente a ferramenta de túnel.

Próximos passos

Este tutorial fornece uma ideia básica de como o sistema de eventos funciona no serviço Azure Web PubSub.

Confira outros tutoriais para se aprofundar em como usar o serviço.