Självstudie: Skapa en chattapp med Azure Web PubSub-tjänsten

I självstudien Publicera och prenumerera på meddelanden lär du dig grunderna i att publicera och prenumerera på meddelanden med Azure Web PubSub. I den här självstudien lär du dig händelsesystemet för Azure Web PubSub och använder det för att skapa ett komplett webbprogram med kommunikationsfunktioner i realtid.

I den här självstudien lär du dig att:

  • Skapa en Web PubSub-tjänstinstans
  • Konfigurera händelsehanterarinställningar för Azure Web PubSub
  • Hanlde-händelser på appservern och skapa en chattapp i realtid

Om du inte har en Azure-prenumeration skapar du ett kostnadsfritt Azure-konto innan du börjar.

Förutsättningar

  • Den här konfigurationen kräver version 2.22.0 eller senare av Azure CLI. Om du använder Azure Cloud Shell är den senaste versionen redan installerad.

Skapa en Azure Web PubSub-instans

Skapa en resursgrupp

En resursgrupp är en logisk container där Azure-resurser distribueras och hanteras. Använd kommandot az group create för att skapa en resursgrupp med namnet myResourceGroupeastus platsen.

az group create --name myResourceGroup --location EastUS

Skapa en Web PubSub-instans

Kör az extension add för att installera eller uppgradera webpubsub-tillägget till den aktuella versionen.

az extension add --upgrade --name webpubsub

Använd kommandot Azure CLI az webpubsub create för att skapa en Web PubSub i resursgruppen som du har skapat. Följande kommando skapar en Free Web PubSub-resurs under resursgruppen myResourceGroup i EastUS:

Viktigt!

Varje Web PubSub-resurs måste ha ett unikt namn. Ersätt <ditt unika resursnamn> med namnet på din Web PubSub i följande exempel.

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

Utdata från det här kommandot visar egenskaperna för den nyligen skapade resursen. Anteckna de två egenskaperna som visas nedan:

  • Resursnamn: Det namn som du angav i parametern --name ovan.
  • hostName: I exemplet är <your-unique-resource-name>.webpubsub.azure.com/värdnamnet .

I det här läget är ditt Azure-konto det enda som har behörighet att utföra åtgärder på den nya resursen.

Hämta Anslut ionString för framtida användning

Viktigt!

En anslutningssträng innehåller den auktoriseringsinformation som krävs för att ditt program ska få åtkomst till Azure Web PubSub-tjänsten. Åtkomstnyckeln i anslutningssträng liknar ett rotlösenord för din tjänst. I produktionsmiljöer bör du alltid vara noga med att skydda dina åtkomstnycklar. Använd Azure Key Vault för att hantera och rotera dina nycklar på ett säkert sätt. Undvik att distribuera åtkomstnycklar till andra användare, hårdkoda dem eller spara dem var som helst i oformaterad text som är tillgänglig för andra. Rotera dina nycklar om du tror att de har komprometterats.

Använd kommandot Azure CLI az webpubsub key för att hämta tjänstens Anslut ionString. <your-unique-resource-name> Ersätt platshållaren med namnet på din Azure Web PubSub-instans.

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

Kopiera anslutningssträng som ska användas senare.

Kopiera den hämtade Anslut ionString och ställ in den i miljövariabeln WebPubSubConnectionString, som självstudien senare läser. Ersätt <connection-string> nedan med den Anslut ionString som du hämtade.

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

Konfigurera projektet

Förutsättningar

Skapa programmet

I Azure Web PubSub finns det två roller, server och klient. Det här konceptet liknar server- och klientrollerna i ett webbprogram. Servern ansvarar för att hantera klienter, lyssna och svara på klientmeddelanden. Klienten ansvarar för att skicka och ta emot användarens meddelanden från servern och visualisera dem för slutanvändare.

I den här självstudien skapar vi ett webbprogram för realtidschatt. I ett riktigt webbprogram omfattar serverns ansvar även att autentisera klienter och hantera statiska webbsidor för programmets användargränssnitt.

Vi använder ASP.NET Core 8 som värd för webbsidorna och hanterar inkommande begäranden.

Först ska vi skapa en ASP.NET Core-webbapp i en chatapp mapp.

  1. Skapa en ny webbapp.

    mkdir chatapp
    cd chatapp
    dotnet new web
    
  2. Lägg till app.UseStaticFiles() Program.cs som stöd för värd för statiska webbsidor.

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.UseStaticFiles();
    
    app.Run();
    
  3. Skapa en HTML-fil och spara den som wwwroot/index.html, vi använder den för chattappens användargränssnitt senare.

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

Du kan testa servern genom att köra dotnet run --urls http://localhost:8080 och komma åt http://localhost:8080/index.html den i webbläsaren.

Lägg till förhandlingsslutpunkt

I självstudien Publicera och prenumerera använder prenumeranten anslutningssträng direkt. I ett verkligt program är det inte säkert att dela anslutningssträng med någon klient, eftersom anslutningssträng har hög behörighet att utföra alla åtgärder i tjänsten. Nu ska vi låta servern använda anslutningssträng och exponera en negotiate slutpunkt för klienten för att hämta den fullständiga URL:en med åtkomsttoken. På så sätt kan servern lägga till autentiseringsmellanprogram före negotiate slutpunkten för att förhindra obehörig åtkomst.

Installera först beroendena.

dotnet add package Microsoft.Azure.WebPubSub.AspNetCore

Nu ska vi lägga till en /negotiate slutpunkt för klienten som ska anropas för att generera 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>() används för att mata in tjänstklienten WebPubSubServiceClient<THub>, med vilken vi kan använda i förhandlingssteget för att generera klientanslutningstoken och i hubbmetoder för att anropa tjänstens REST-API:er när hubbhändelser utlöses. Den här tokengenereringskoden liknar den som vi använde i självstudien publicera och prenumerera på meddelande, förutom att vi skickar ytterligare ett argument (userId) när du genererar token. Användar-ID kan användas för att identifiera klientens identitet, så när du får ett meddelande vet du var meddelandet kommer ifrån.

Koden läser anslutningssträng från miljövariabeln WebPubSubConnectionString som vi angav i föregående steg.

Kör servern igen med .dotnet run --urls http://localhost:8080

Du kan testa det här API:et genom att http://localhost:8080/negotiate?id=user1 komma åt och det ger dig den fullständiga URL:en för Azure Web PubSub med en åtkomsttoken.

Hantera händelser

När det finns vissa aktiviteter på klientsidan i Azure Web PubSub (till exempel en klient ansluter, ansluter, kopplar från eller en klient skickar meddelanden) skickar tjänsten meddelanden till servern så att den kan reagera på dessa händelser.

Händelser levereras till servern i form av Webhook. Webhook hanteras och exponeras av programservern och registreras på Azure Web PubSub-tjänstsidan. Tjänsten anropar webhooks när en händelse inträffar.

Azure Web PubSub följer CloudEvents för att beskriva händelsedata.

Nedan hanterar connected vi systemhändelser när en klient är ansluten och hanterar message användarhändelser när en klient skickar meddelanden för att skapa chattappen.

Web PubSub SDK för AspNetCore Microsoft.Azure.WebPubSub.AspNetCore som vi installerade i föregående steg kan också hjälpa dig att parsa och bearbeta CloudEvents-begäranden.

Lägg först till händelsehanterare före app.Run(). Ange slutpunktssökvägen för händelserna, låt oss säga /eventhandler.

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

I den klass Sample_ChatApp som vi skapade i föregående steg lägger du nu till en konstruktor som WebPubSubServiceClient<Sample_ChatApp> vi använder för att anropa Web PubSub-tjänsten. Och OnConnectedAsync() för att svara när connected händelsen utlöses, OnMessageReceivedAsync() för att hantera meddelanden från klienten.

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

I koden ovan använder vi tjänstklienten för att sända ett meddelande i JSON-format till alla som är anslutna till SendToAllAsync.

Uppdatera webbsidan

Nu ska vi uppdatera index.html för att lägga till logiken för att ansluta, skicka meddelande och visa mottagna meddelanden på sidan.

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

Du kan se i koden ovan att vi ansluter använda det interna WebSocket-API:et i webbläsaren och använda WebSocket.send() för att skicka meddelanden och WebSocket.onmessage lyssna på mottagna meddelanden.

Du kan också använda klient-SDK:er för att ansluta till tjänsten, vilket ger dig automatisk återanslutning, felhantering med mera.

Det finns nu ett steg kvar för chatten att fungera. Nu ska vi konfigurera vilka händelser vi bryr oss om och var händelserna ska skickas till i tjänsten Web PubSub.

Konfigurera händelsehanteraren

Vi ställer in händelsehanteraren i Web PubSub-tjänsten för att tala om för tjänsten var händelserna ska skickas.

Hur anropar Web PubSub-tjänsten localhost om den inte har någon internettillgänglig slutpunkt när webbservern körs lokalt? Det finns vanligtvis två sätt. Den ena är att exponera localhost för allmänheten med hjälp av något allmänt tunnelverktyg, och det andra är att använda awps-tunnel för att tunneltrafik från Web PubSub-tjänsten via verktyget till din lokala server.

I det här avsnittet använder vi Azure CLI för att ange händelsehanterare och använda awps-tunnel för att dirigera trafik till localhost.

Konfigurera hubbinställningar

Vi anger att URL-mallen ska använda tunnel schemat så att Web PubSub dirigerar meddelanden via tunnelanslutningen awps-tunnel. Händelsehanterare kan ställas in från antingen portalen eller CLI enligt beskrivningen i den här artikeln, här anger vi det via CLI. Eftersom vi lyssnar på händelser i sökvägen /eventhandler som föregående steguppsättningar anger vi URL-mallen till tunnel:///eventhandler.

Använd azure CLI az webpubsub hub create-kommandot för att skapa händelsehanterarinställningarna för Sample_ChatApp hubben.

Viktigt!

Ersätt <ditt unika resursnamn> med namnet på din Web PubSub-resurs som skapades från föregående steg.

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"

Kör awps-tunnel lokalt

Ladda ned och installera awps-tunnel

Verktyget körs på Node.js version 16 eller senare.

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

Använd tjänsten anslutningssträng och kör

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

Kör webbservern

Nu är allt klart. Nu ska vi köra webbservern och leka med chattappen i praktiken.

Kör nu servern med .dotnet run --urls http://localhost:8080

Det fullständiga kodexemplet i den här självstudien finns här.

Öppna http://localhost:8080/index.html. Du kan ange ditt användarnamn och börja chatta.

Lazy Auth med connect händelsehanterare

I föregående avsnitt visar vi hur du använder förhandlingsslutpunkten för att returnera url:en för Web PubSub-tjänsten och JWT-åtkomsttoken för klienterna för att ansluta till Web PubSub-tjänsten. I vissa fall, till exempel gränsenheter som har begränsade resurser, kan klienter föredra direktanslutning till Web PubSub-resurser. I sådana fall kan du konfigurera connect händelsehanteraren för att lata autentisera klienterna, tilldela användar-ID till klienterna, ange de grupper som klienterna ansluter när de ansluter, konfigurera de behörigheter som klienterna har och WebSocket-delprotokol som WebSocket-svar på klienten osv. Information finns i avsnittet om att ansluta händelsehanterare.

Nu ska vi använda connect händelsehanteraren för att uppnå det som liknar det som avsnittet förhandlar om .

Uppdatera hubbinställningar

Först ska vi uppdatera hubbinställningarna för att även inkludera connect händelsehanterare. Vi måste också tillåta anonym anslutning så att klienter utan JWT-åtkomsttoken kan ansluta till tjänsten.

Använd azure CLI az webpubsub hub update-kommandot för att skapa händelsehanterarinställningarna för Sample_ChatApp hubben.

Viktigt!

Ersätt <ditt unika resursnamn> med namnet på din Web PubSub-resurs som skapades från föregående steg.

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"

Uppdatera uppströmslogik för att hantera anslutningshändelse

Nu ska vi uppdatera den överordnade logiken för att hantera anslutningshändelsen. Vi kan också ta bort förhandlingsslutpunkten nu.

Som liknar det vi gör i förhandlar slutpunkt som demosyfte läser vi även ID från frågeparametrarna. I connect-händelsen bevaras den ursprungliga klientfrågan i connect event requet-brödtexten.

I klassen Sample_ChatAppåsidosätter du OnConnectAsync() för att hantera connect händelsen:

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

Uppdatera index.html för direktanslutning

Nu ska vi uppdatera webbsidan för att direktansluta till Web PubSub-tjänsten. En sak att nämna är att nu för demo syfte Web PubSub tjänstslutpunkten är hårdkodad i klientkoden, uppdatera tjänstens värdnamn <the host name of your service> i htmlen nedan med värdet från din egen tjänst. Det kan fortfarande vara användbart att hämta slutpunktsvärdet för Web PubSub-tjänsten från servern. Det ger dig mer flexibilitet och kontrollbarhet till den punkt där klienten ansluter till.

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

Kör servern igen

Kör nu servern igen och besök webbsidan enligt anvisningarna innan. Om du har stoppat awps-tunnelkör du även tunnelverktyget igen.

Nästa steg

I den här självstudien får du en grundläggande uppfattning om hur händelsesystemet fungerar i Azure Web PubSub-tjänsten.

Kontrollera andra självstudier för att ytterligare undersöka hur du använder tjänsten.