Esercitazione: Creare un'app di chat con il servizio PubSub Web di Azure

Nell'esercitazione Pubblica e sottoscrivi messaggio vengono fornite informazioni di base sulla pubblicazione e la sottoscrizione dei messaggi con PubSub Web di Azure. In questa esercitazione si apprenderà il sistema di eventi di Azure Web PubSub e lo si userà per creare un'applicazione Web completa con funzionalità di comunicazione in tempo reale.

In questa esercitazione apprenderai a:

  • Creare un'istanza del servizio Web PubSub
  • Configurare le impostazioni del gestore eventi per PubSub Web di Azure
  • Eventi Hanlde nel server app e creare un'app di chat in tempo reale

Se non si ha una sottoscrizione di Azure, creare un account Azure gratuito prima di iniziare.

Prerequisiti

  • Questa configurazione richiede la versione 2.22.0 o successiva dell'interfaccia della riga di comando di Azure. Se si usa Azure Cloud Shell, la versione più recente è già installata.

Creare un'istanza di PubSub Di Azure

Creare un gruppo di risorse

Un gruppo di risorse è un contenitore logico in cui vengono distribuite e gestite le risorse di Azure. Usare il comando az group create per creare un gruppo di risorse denominato myResourceGroup nel eastus percorso.

az group create --name myResourceGroup --location EastUS

Creare un'istanza di Web PubSub

Eseguire az extension add per installare o aggiornare l'estensione webpubsub alla versione corrente.

az extension add --upgrade --name webpubsub

Usare il comando az webpubsub create dell'interfaccia della riga di comando di Azure per creare un Web PubSub nel gruppo di risorse creato. Il comando seguente crea una risorsa Web PubSub gratuita nel gruppo di risorse myResourceGroup in EastUS:

Importante

Ogni risorsa Web PubSub deve avere un nome univoco. Sostituire <your-unique-resource-name> con il nome di Web PubSub negli esempi seguenti.

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

L'output di questo comando mostra le proprietà della risorsa appena creata. Prendere nota delle due proprietà elencate di seguito:

  • Nome risorsa: nome specificato al --name parametro precedente.
  • hostName: nell'esempio il nome host è <your-unique-resource-name>.webpubsub.azure.com/.

A questo punto, l'account Azure è l'unico autorizzato a eseguire qualsiasi operazione su questa nuova risorsa.

Ottenere il Connessione ionString per un uso futuro

Importante

Un stringa di connessione include le informazioni di autorizzazione necessarie per l'applicazione per accedere al servizio Web PubSub di Azure. La chiave di accesso all'interno del stringa di connessione è simile a una password radice per il servizio. Negli ambienti di produzione, prestare sempre attenzione a proteggere le chiavi di accesso. Usare Azure Key Vault per gestire e ruotare le chiavi in modo sicuro. Evitare di distribuire chiavi di accesso ad altri utenti, hardcodedarli o salvarli in qualsiasi punto del testo normale accessibile ad altri utenti. Ruotare le chiavi se si ritiene che siano state compromesse.

Usare il comando az webpubsub key dell'interfaccia della riga di comando di Azure per ottenere il Connessione ionString del servizio. Sostituire il <your-unique-resource-name> segnaposto con il nome dell'istanza di PubSub Web di Azure.

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

Copiare il stringa di connessione da usare in un secondo momento.

Copiare il Connessione ionString recuperato e impostarlo nella variabile di ambiente , che l'esercitazione WebPubSubConnectionStringlegge in seguito. Sostituire <connection-string> di seguito con il Connessione ionString recuperato.

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

Configurare il progetto

Prerequisiti

Creare l'applicazione

In Azure Web PubSub sono disponibili due ruoli, server e client. Questo concetto è simile ai ruoli server e client in un'applicazione Web. Il server è responsabile della gestione dei client, dell'ascolto e della risposta ai messaggi client. Il client è responsabile dell'invio e della ricezione dei messaggi dell'utente dal server e della relativa visualizzazione per l'utente finale.

In questa esercitazione viene creata un'applicazione Web chat in tempo reale. In un'applicazione Web reale, la responsabilità del server include anche l'autenticazione dei client e la gestione di pagine Web statiche per l'interfaccia utente dell'applicazione.

Usiamo ASP.NET Core 8 per ospitare le pagine Web e gestire le richieste in ingresso.

Creare prima di tutto un'app Web ASP.NET Core in una chatapp cartella.

  1. Creare una nuova app Web.

    mkdir chatapp
    cd chatapp
    dotnet new web
    
  2. Aggiungere app.UseStaticFiles() Program.cs per supportare l'hosting di pagine Web statiche.

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.UseStaticFiles();
    
    app.Run();
    
  3. Creare un file HTML e salvarlo come wwwroot/index.html, lo usiamo per l'interfaccia utente dell'app di chat in un secondo momento.

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

È possibile testare il server eseguendo dotnet run --urls http://localhost:8080 e accedendo http://localhost:8080/index.html nel browser.

Aggiungere l'endpoint di negoziazione

Nell'esercitazione Pubblicare e sottoscrivere il messaggio, il sottoscrittore utilizza direttamente stringa di connessione. In un'applicazione reale non è sicuro condividere il stringa di connessione con qualsiasi client, perché stringa di connessione ha privilegi elevati per eseguire qualsiasi operazione al servizio. A questo punto, si avrà il server che usa il stringa di connessione ed esponendo un negotiate endpoint per il client per ottenere l'URL completo con il token di accesso. In questo modo, il server può aggiungere middleware di autenticazione prima dell'endpoint per impedire l'accesso negotiate non autorizzato.

Installare prima di tutto le dipendenze.

dotnet add package Microsoft.Azure.WebPubSub.AspNetCore

Aggiungere ora un /negotiate endpoint per il client per chiamare per generare il 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>() viene usato per inserire il client WebPubSubServiceClient<THub>del servizio , con cui è possibile usare nel passaggio di negoziazione per generare il token di connessione client e nei metodi hub per richiamare le API REST del servizio quando vengono attivati gli eventi dell'hub. Questo codice di generazione di token è simile a quello usato nell'esercitazione sui messaggi di pubblicazione e sottoscrizione, ad eccezione del passaggio di un altro argomento (userId) durante la generazione del token. L'ID utente può essere usato per identificare l'identità del client in modo da ricevere un messaggio da cui si sa da dove proviene il messaggio.

Il codice legge stringa di connessione dalla variabile di ambiente impostata WebPubSubConnectionString nel passaggio precedente.

Rieseguire il server usando dotnet run --urls http://localhost:8080.

È possibile testare questa API accedendo http://localhost:8080/negotiate?id=user1 e fornisce l'URL completo di Azure Web PubSub con un token di accesso.

Gestire eventi

In Web PubSub di Azure, quando si verificano determinate attività sul lato client (ad esempio, un client si connette, è connesso, disconnesso o un client invia messaggi), il servizio invia notifiche al server in modo che possa reagire a questi eventi.

Gli eventi vengono recapitati al server sotto forma di webhook. Il webhook viene gestito ed esposto dal server applicazioni e registrato sul lato servizio Web PubSub di Azure. Il servizio richiama i webhook ogni volta che si verifica un evento.

Web PubSub di Azure segue CloudEvents per descrivere i dati dell'evento.

Di seguito vengono gestiti connected gli eventi di sistema quando un client è connesso e gestisce message gli eventi utente quando un client invia messaggi per compilare l'app di chat.

Web PubSub SDK per AspNetCore Microsoft.Azure.WebPubSub.AspNetCore installato nel passaggio precedente può anche aiutare a analizzare ed elaborare le richieste CloudEvents.

Prima di tutto, aggiungere gestori eventi prima app.Run()di . Specificare il percorso dell'endpoint per gli eventi, ad /eventhandleresempio .

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

Ora, all'interno della classe Sample_ChatApp creata nel passaggio precedente, aggiungere un costruttore da usare WebPubSubServiceClient<Sample_ChatApp> per richiamare il servizio Web PubSub. E OnConnectedAsync() per rispondere quando connected viene attivato l'evento, OnMessageReceivedAsync() per gestire i messaggi dal client.

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

Nel codice precedente viene usato il client del servizio per trasmettere un messaggio di notifica in formato JSON a tutti i quali viene aggiunto a SendToAllAsync.

Aggiornare la pagina Web

Si aggiornerà index.html ora per aggiungere la logica per connettersi, inviare messaggi e visualizzare i messaggi ricevuti nella pagina.

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

Nel codice precedente viene usata l'API WebSocket nativa nel browser e viene usata WebSocket.send() per inviare messaggi e WebSocket.onmessage per ascoltare i messaggi ricevuti.

È anche possibile usare gli SDK client per connettersi al servizio, che consente di riconnettersi automaticamente, gestire gli errori e altro ancora.

C'è ora un passo a sinistra per il lavoro della chat. È possibile configurare gli eventi a cui si è interessati e dove inviare gli eventi nel servizio Web PubSub.

Configurare il gestore eventi

Il gestore eventi nel servizio Web PubSub viene impostato per indicare al servizio dove inviare gli eventi.

Quando il server Web viene eseguito localmente, in che modo il servizio Web PubSub richiama localhost se non dispone di un endpoint accessibile da Internet? Esistono in genere due modi. Uno consiste nell'esporre localhost al pubblico usando alcuni strumenti di tunnel generale e l'altro consiste nell'usare awps-tunnel per eseguire il tunneling del traffico dal servizio Web PubSub attraverso lo strumento al server locale.

In questa sezione si usa l'interfaccia della riga di comando di Azure per impostare i gestori eventi e usare awps-tunnel per instradare il traffico a localhost.

Configurare le impostazioni dell'hub

Il modello di URL viene impostato in modo da usare tunnel lo schema in modo che Web PubSub instrada i messaggi attraverso la connessione tunnel awps-tunnel. I gestori eventi possono essere impostati dal portale o dall'interfaccia della riga di comando, come descritto in questo articolo, per impostarlo tramite l'interfaccia della riga di comando. Poiché gli eventi vengono ascoltati nel percorso /eventhandler come set di passaggi precedenti, il modello di URL viene impostato su tunnel:///eventhandler.

Usare il comando az webpubsub hub create dell'interfaccia della riga di comando di Azure per creare le impostazioni del gestore eventi per l'hubSample_ChatApp.

Importante

Sostituire <your-unique-resource-name> con il nome della risorsa Web PubSub creata nei passaggi precedenti.

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"

Eseguire awps-tunnel in locale

Scaricare e installare awps-tunnel

Lo strumento viene eseguito in Node.js versione 16 o successiva.

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

Usare il servizio stringa di connessione ed eseguire

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

Eseguire il server Web

Ora tutto è impostato. Eseguire il server Web e giocare con l'app chat in azione.

Eseguire ora il server usando dotnet run --urls http://localhost:8080.

L'esempio di codice completo di questa esercitazione è disponibile qui.

http://localhost:8080/index.html aperti. È possibile immettere il nome utente e avviare la chat.

Autenticazione differita con connect gestore eventi

Nelle sezioni precedenti viene illustrato come usare l'endpoint negotiate per restituire l'URL del servizio Web PubSub e il token di accesso JWT per i client per connettersi al servizio Web PubSub. In alcuni casi, ad esempio, i dispositivi perimetrali con risorse limitate, i client potrebbero preferire la connessione diretta alle risorse PubSub Web. In questi casi, è possibile configurare connect il gestore eventi per l'autenticazione differita dei client, assegnare l'ID utente ai client, specificare i gruppi aggiunti ai client dopo la connessione, configurare le autorizzazioni che i client hanno e il sottoprotocolo WebSocket come risposta WebSocket al client e così via. I dettagli fanno riferimento alla specifica del gestore eventi di connessione.

A questo punto si userà connect il gestore eventi per ottenere un risultato simile a quello della sezione negotiate .

Aggiornare le impostazioni dell'hub

Prima di tutto, aggiornare le impostazioni dell'hub per includere connect anche il gestore eventi, è necessario consentire anche la connessione anonima in modo che i client senza token di accesso JWT possano connettersi al servizio.

Usare il comando az webpubsub hub update dell'interfaccia della riga di comando di Azure per creare le impostazioni del gestore eventi per l'hubSample_ChatApp.

Importante

Sostituire <your-unique-resource-name> con il nome della risorsa Web PubSub creata nei passaggi precedenti.

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"

Aggiornare la logica upstream per gestire l'evento di connessione

È ora possibile aggiornare la logica upstream per gestire l'evento di connessione. È anche possibile rimuovere l'endpoint di negoziazione ora.

Analogamente alle operazioni eseguite nell'endpoint di negoziazione come scopo dimostrativo, viene letto anche l'ID dai parametri di query. In caso di connessione, la query client originale viene mantenuta nel corpo del requet event connect.

All'interno della classe Sample_ChatApp, eseguire l'override OnConnectAsync() per gestire connect l'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();
    }
}

Aggiornare index.html per la connessione diretta

A questo punto, aggiornare la pagina Web per connettersi direttamente al servizio Web PubSub. Un aspetto da ricordare è che ora a scopo dimostrativo l'endpoint del servizio Web PubSub è hardcoded nel codice client, aggiornare il nome <the host name of your service> host del servizio nel codice HTML seguente con il valore del proprio servizio. Potrebbe essere comunque utile recuperare il valore dell'endpoint del servizio Web PubSub dal server, offre maggiore flessibilità e controllabilità a dove si connette il client.

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

Rieseguire il server

Eseguire di nuovo il server e visitare la pagina Web seguendo le istruzioni riportate in precedenza. Se è stato arrestato awps-tunnel, eseguire nuovamente lo strumento tunnel.

Passaggi successivi

Questa esercitazione offre un'idea di base del funzionamento del sistema eventi nel servizio Web PubSub di Azure.

Vedere altre esercitazioni per approfondire l'uso del servizio.