Samouczek: tworzenie aplikacji do czatu za pomocą usługi Azure Web PubSub

W samouczku Publikowanie i subskrybowanie komunikatów poznasz podstawy publikowania i subskrybowania komunikatów za pomocą usługi Azure Web PubSub. W tym samouczku poznasz system zdarzeń usługi Azure Web PubSub i użyjesz go do utworzenia kompletnej aplikacji internetowej z funkcją komunikacji w czasie rzeczywistym.

Z tego samouczka dowiesz się, jak wykonywać następujące czynności:

  • Tworzenie wystąpienia usługi Web PubSub
  • Konfigurowanie ustawień programu obsługi zdarzeń dla usługi Azure Web PubSub
  • Zdarzenia Hanlde na serwerze aplikacji i tworzenie aplikacji czatu w czasie rzeczywistym

Jeśli nie masz subskrypcji platformy Azure, przed rozpoczęciem utwórz bezpłatne konto platformy Azure.

Wymagania wstępne

  • Ta konfiguracja wymaga wersji 2.22.0 lub nowszej interfejsu wiersza polecenia platformy Azure. W przypadku korzystania z usługi Azure Cloud Shell najnowsza wersja jest już zainstalowana.

Tworzenie wystąpienia usługi Azure Web PubSub

Tworzenie grupy zasobów

Grupa zasobów to logiczny kontener przeznaczony do wdrażania zasobów platformy Azure i zarządzania nimi. Użyj polecenia az group create, aby utworzyć grupę zasobów o nazwie myResourceGroup w eastus lokalizacji.

az group create --name myResourceGroup --location EastUS

Tworzenie wystąpienia usługi Web PubSub

Uruchom polecenie az extension add , aby zainstalować lub uaktualnić rozszerzenie webpubsub do bieżącej wersji.

az extension add --upgrade --name webpubsub

Użyj polecenia az webpubsub create interfejsu wiersza polecenia platformy Azure, aby utworzyć internetowy pubSub w utworzonej grupie zasobów. Następujące polecenie tworzy zasób Free Web PubSub w grupie zasobów myResourceGroup w regionie EastUS:

Ważne

Każdy zasób Web PubSub musi mieć unikatową nazwę. Zastąp <ciąg your-unique-resource-name> nazwą usługi Web PubSub w poniższych przykładach.

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

Dane wyjściowe tego polecenia pokazują właściwości nowo utworzonego zasobu. Zanotuj dwie poniższe właściwości:

  • Nazwa zasobu: nazwa podana powyżej parametru --name .
  • hostName: w przykładzie nazwa hosta to <your-unique-resource-name>.webpubsub.azure.com/.

W tym momencie Twoje konto platformy Azure jest jedynym autoryzowanym do wykonywania jakichkolwiek operacji na tym nowym zasobie.

Pobieranie Połączenie ionString do użycia w przyszłości

Ważne

Parametry połączenia zawiera informacje o autoryzacji wymagane przez aplikację w celu uzyskania dostępu do usługi Azure Web PubSub. Klucz dostępu wewnątrz parametry połączenia jest podobny do hasła głównego usługi. W środowiskach produkcyjnych należy zawsze zachować ostrożność w celu ochrony kluczy dostępu. Usługa Azure Key Vault umożliwia bezpieczne zarządzanie kluczami i obracanie ich. Unikaj dystrybuowania kluczy dostępu do innych użytkowników, kodowania ich lub zapisywania ich w dowolnym miejscu w postaci zwykłego tekstu, który jest dostępny dla innych użytkowników. Obracanie kluczy, jeśli uważasz, że mogły one zostać naruszone.

Użyj polecenia az webpubsub key interfejsu wiersza polecenia platformy Azure, aby uzyskać Połączenie ionString usługi. Zastąp <your-unique-resource-name> symbol zastępczy nazwą wystąpienia usługi Azure Web PubSub.

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

Skopiuj parametry połączenia do późniejszego użycia.

Skopiuj pobrany Połączenie ionString i ustaw go w zmiennej środowiskowej WebPubSubConnectionString, którą później odczytuje samouczek. Zastąp <connection-string> poniżej pobranym ciągiem Połączenie ionString.

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

Konfigurowanie projektu

Wymagania wstępne

Tworzenie aplikacji

W usłudze Azure Web PubSub istnieją dwie role, serwer i klient. Ta koncepcja jest podobna do ról serwera i klienta w aplikacji internetowej. Serwer jest odpowiedzialny za zarządzanie klientami, nasłuchiwanie i odpowiadanie na komunikaty klienta. Klient jest odpowiedzialny za wysyłanie i odbieranie komunikatów użytkownika z serwera i wizualizowanie ich dla użytkownika końcowego.

W tym samouczku utworzymy aplikację internetową do czatu w czasie rzeczywistym. W prawdziwej aplikacji internetowej odpowiedzialność serwera obejmuje również uwierzytelnianie klientów i obsługę statycznych stron internetowych dla interfejsu użytkownika aplikacji.

Używamy ASP.NET Core 8 do hostowania stron internetowych i obsługi żądań przychodzących.

Najpierw utwórzmy aplikację internetową ASP.NET Core w folderze chatapp .

  1. Utwórz nową aplikację internetową.

    mkdir chatapp
    cd chatapp
    dotnet new web
    
  2. Dodaj app.UseStaticFiles() Program.cs do obsługi hostowania statycznych stron internetowych.

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.UseStaticFiles();
    
    app.Run();
    
  3. Utwórz plik HTML i zapisz go jako wwwroot/index.html, użyjemy go do interfejsu użytkownika aplikacji czatu później.

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

Serwer można przetestować, uruchamiając polecenie dotnet run --urls http://localhost:8080 i dostęp http://localhost:8080/index.html w przeglądarce.

Dodawanie negocjowanego punktu końcowego

W samouczku Publikowanie i subskrybowanie subskrybent korzysta bezpośrednio z parametry połączenia. W rzeczywistej aplikacji nie można bezpiecznie udostępniać parametry połączenia żadnemu klientowi, ponieważ parametry połączenia ma wysokie uprawnienia do wykonywania dowolnej operacji w usłudze. Teraz serwer korzysta z parametry połączenia i uwidacznia negotiate punkt końcowy dla klienta, aby uzyskać pełny adres URL z tokenem dostępu. W taki sposób serwer może dodać oprogramowanie pośredniczące uwierzytelniania przed negotiate punktem końcowym, aby zapobiec nieautoryzowanemu dostępowi.

Najpierw zainstaluj zależności.

dotnet add package Microsoft.Azure.WebPubSub.AspNetCore

Teraz dodajmy /negotiate punkt końcowy dla klienta w celu wywołania metody w celu wygenerowania tokenu.

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>() Służy do wstrzykiwania klienta WebPubSubServiceClient<THub>usługi , za pomocą którego możemy użyć w kroku negocjacji do generowania tokenu połączenia klienta i w metodach centrum do wywoływania interfejsów API REST usługi po wyzwoleniu zdarzeń centrum. Ten kod generowania tokenu jest podobny do kodu użytego w samouczku dotyczącym publikowania i subskrybowania komunikatów, z wyjątkiem przekazywania jeszcze jednego argumentu (userId) podczas generowania tokenu. Identyfikator użytkownika może służyć do identyfikowania tożsamości klienta, więc po otrzymaniu komunikatu wiadomo, skąd pochodzi komunikat.

Kod odczytuje parametry połączenia ze zmiennej środowiskowej WebPubSubConnectionString ustawionej w poprzednim kroku.

Uruchom ponownie serwer przy użyciu polecenia dotnet run --urls http://localhost:8080.

Możesz przetestować ten interfejs API, korzystając http://localhost:8080/negotiate?id=user1 z dostępu i podając pełny adres URL usługi Azure Web PubSub z tokenem dostępu.

Obsługa zdarzeń

W usłudze Azure Web PubSub, gdy po stronie klienta występują pewne działania (na przykład klient łączy się, łączy, rozłącza lub wysyła komunikaty), usługa wysyła powiadomienia do serwera, aby mogła reagować na te zdarzenia.

Zdarzenia są dostarczane do serwera w postaci elementu webhook. Element webhook jest obsługiwany i udostępniany przez serwer aplikacji i zarejestrowany po stronie usługi Azure Web PubSub. Usługa wywołuje elementy webhook za każdym razem, gdy wystąpi zdarzenie.

Usługa Azure Web PubSub jest zgodna z usługą CloudEvents , aby opisać dane zdarzenia.

Poniżej obsługujemy connected zdarzenia systemowe, gdy klient jest połączony i obsługuje message zdarzenia użytkownika, gdy klient wysyła komunikaty w celu skompilowania aplikacji do czatu.

Zestaw WEB PubSub SDK dla aplikacji AspNetCore Microsoft.Azure.WebPubSub.AspNetCore zainstalowany w poprzednim kroku może również pomóc w analizowaniu i przetwarzaniu żądań CloudEvents.

Najpierw dodaj programy obsługi zdarzeń przed .app.Run() Określ ścieżkę punktu końcowego dla zdarzeń, powiedzmy /eventhandler.

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

Teraz wewnątrz klasy Sample_ChatApp utworzonej w poprzednim kroku dodaj konstruktor do pracy, WebPubSubServiceClient<Sample_ChatApp> którego używamy do wywoływania usługi Web PubSub. Aby OnConnectedAsync() reagować, gdy connected zdarzenie jest wyzwalane, OnMessageReceivedAsync() aby obsługiwać komunikaty z klienta.

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

W powyższym kodzie używamy klienta usługi do emisji komunikatu powiadomienia w formacie JSON do wszystkich, do których jest przyłączony SendToAllAsync.

Aktualizowanie strony internetowej

Teraz zaktualizujmy index.html logikę w celu nawiązania połączenia, wysyłania komunikatów i wyświetlania odebranych komunikatów na stronie.

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

W powyższym kodzie można zobaczyć, że używamy natywnego interfejsu API protokołu WebSocket w przeglądarce i używamy WebSocket.send() go do wysyłania komunikatów i WebSocket.onmessage nasłuchiwania odebranych komunikatów.

Możesz również użyć zestawów SDK klienta, aby nawiązać połączenie z usługą, co umożliwia automatyczne ponowne nawiązywanie połączenia, obsługę błędów i nie tylko.

Teraz pozostało jeszcze jeden krok, aby czat działał. Skonfigurujmy zdarzenia, których dotyczymy, i gdzie mają być wysyłane zdarzenia w usłudze Web PubSub.

Konfigurowanie programu obsługi zdarzeń

Ustawiamy program obsługi zdarzeń w usłudze Web PubSub, aby poinformować usługę, do której mają być wysyłane zdarzenia.

Kiedy serwer internetowy działa lokalnie, jak usługa Web PubSub wywołuje hosta lokalnego, jeśli nie ma punktu końcowego dostępnego dla Internetu? Zwykle istnieją dwa sposoby. Jednym z nich jest uwidocznienie hosta lokalnego publicznie przy użyciu ogólnego narzędzia tunelu, a drugi jest użycie tunelu awps-tunnel do tunelowania ruchu z usługi Web PubSub za pośrednictwem narzędzia do serwera lokalnego.

W tej sekcji użyjemy interfejsu wiersza polecenia platformy Azure, aby ustawić programy obsługi zdarzeń i użyć narzędzia awps-tunnel do kierowania ruchu do hosta lokalnego.

Konfigurowanie ustawień centrum

Ustawiliśmy szablon adresu URL tak, aby usługa tunnel Web PubSub kieruje komunikaty za pośrednictwem połączenia tunelu awps-tunnel. Procedury obsługi zdarzeń można ustawić z poziomu portalu lub interfejsu wiersza polecenia zgodnie z opisem w tym artykule. W tym miejscu ustawiliśmy je za pomocą interfejsu wiersza polecenia. Ponieważ nasłuchujemy zdarzeń w ścieżce /eventhandler jako poprzednie zestawy kroków, ustawiliśmy szablon adresu URL na tunnel:///eventhandlerwartość .

Użyj polecenia az webpubsub hub create interfejsu wiersza polecenia platformy Azure, aby utworzyć ustawienia programu obsługi zdarzeń dla Sample_ChatApp centrum.

Ważne

Zastąp <ciąg your-unique-resource-name> nazwą zasobu Web PubSub utworzonego na podstawie poprzednich kroków.

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"

Uruchamianie aplikacji awps-tunnel lokalnie

Pobieranie i instalowanie aplikacji awps-tunnel

Narzędzie działa w Node.js w wersji 16 lub nowszej.

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

Używanie parametry połączenia usługi i uruchamianie

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

Uruchamianie serwera internetowego

Teraz wszystko jest ustawione. Uruchommy serwer internetowy i odtwórzmy działanie aplikacji do czatu.

Teraz uruchom serwer przy użyciu polecenia dotnet run --urls http://localhost:8080.

Kompletny przykład kodu tego samouczka można znaleźć tutaj.

Otwórz http://localhost:8080/index.html. Możesz wprowadzić nazwę użytkownika i rozpocząć rozmowę.

Uwierzytelnianie z opóźnieniem z procedurą obsługi zdarzeń connect

W poprzednich sekcjach pokazano, jak używać punktu końcowego negocjowania w celu zwrócenia adresu URL usługi Web PubSub i tokenu dostępu JWT dla klientów w celu nawiązania połączenia z usługą Web PubSub. W niektórych przypadkach na przykład urządzenia brzegowe, które mają ograniczone zasoby, klienci mogą preferować bezpośrednie połączenie z zasobami Web PubSub. W takich przypadkach można skonfigurować connect procedurę obsługi zdarzeń w celu uwierzytelnienia klientów z opóźnieniem, przypisania identyfikatora użytkownika do klientów, określenia grup, które klienci dołączają po nawiązaniu połączenia, skonfigurowania uprawnień, które klienci mają, oraz podprotocol protokołu WebSocket jako odpowiedzi protokołu WebSocket na klienta itp. Szczegółowe informacje można znaleźć w celu nawiązania połączenia ze specyfikacją programu obsługi zdarzeń.

Teraz użyjemy connect programu obsługi zdarzeń, aby osiągnąć to, co robi sekcja negocjowania.

Aktualizowanie ustawień centrum

Najpierw zaktualizujmy ustawienia centrum, aby uwzględnić connect również procedurę obsługi zdarzeń, musimy również zezwolić na anonimowe łączenie, aby klienci bez tokenu dostępu JWT mogli łączyć się z usługą.

Użyj polecenia az webpubsub hub update interfejsu wiersza polecenia platformy Azure, aby utworzyć ustawienia programu obsługi zdarzeń dla Sample_ChatApp centrum.

Ważne

Zastąp <ciąg your-unique-resource-name> nazwą zasobu Web PubSub utworzonego na podstawie poprzednich kroków.

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"

Aktualizowanie logiki nadrzędnej w celu obsługi zdarzenia połączenia

Teraz zaktualizujmy logikę nadrzędną, aby obsłużyć zdarzenie połączenia. Możemy również usunąć teraz punkt końcowy negocjatowania.

Podobnie jak w przypadku negocjowania punktu końcowego jako celu demonstracyjnego, odczytamy również identyfikator z parametrów zapytania. W przypadku połączenia oryginalne zapytanie klienta jest zachowywane w treści requet zdarzenia połączenia.

Wewnątrz klasy Sample_ChatAppzastąpij OnConnectAsync() , aby obsłużyć connect zdarzenie:

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

Aktualizowanie index.html do bezpośredniego połączenia

Teraz zaktualizujmy stronę internetową, aby bezpośrednio nawiązać połączenie z usługą Web PubSub. Jedną z rzeczy, o których należy wspomnieć, jest to, że teraz w celu demonstracyjnego punktu końcowego usługi PubSub jest zakodowany w kodzie klienta, zaktualizuj nazwę <the host name of your service> hosta usługi w poniższym kodzie HTML przy użyciu wartości z własnej usługi. Nadal przydatne może być pobranie wartości punktu końcowego usługi Web PubSub z serwera, co zapewnia większą elastyczność i możliwość kontrolowania lokalizacji, z którą klient nawiązuje połączenie.

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

Ponowne uruchamianie serwera

Teraz ponownie uruchom serwer i odwiedź stronę internetową zgodnie z instrukcjami przed. Jeśli zatrzymano awps-tunnelprogram , uruchom ponownie narzędzie tunelu.

Następne kroki

Ten samouczek zawiera podstawowe informacje na temat działania systemu zdarzeń w usłudze Azure Web PubSub.

Zapoznaj się z innymi samouczkami, aby dowiedzieć się więcej na temat korzystania z usługi.