Tutorial: Erstellen einer serverlosen Echtzeit-Chat-App mit Azure Functions und dem Azure Web PubSub-Dienst

Der Azure Web PubSub-Dienst unterstützt Sie beim einfachen Erstellen von Echtzeit-Messagingwebanwendungen mithilfe von WebSockets und des Veröffentlichen-Abonnieren-Musters. Azure Functions ist eine serverlose Plattform, mit der Sie Ihren Code ohne Verwaltung von Infrastruktur ausführen können. In diesem Tutorial erfahren Sie, wie Sie den Azure Web PubSub-Dienst und Azure Functions verwenden, um eine serverlose Anwendung mit Echtzeitmessaging und dem Veröffentlichen-Abonnieren-Muster zu erstellen.

In diesem Tutorial lernen Sie Folgendes:

  • Erstellen einer serverlosen Echtzeit-Chat-App
  • Arbeiten mit Web PubSub-Funktionstrigger- und -ausgabebindungen
  • Bereitstellen der Funktion in der Azure-Funktions-App
  • Konfigurieren der Azure-Authentifizierung
  • Konfigurieren des Web PubSub-Ereignishandlers zum Weiterleiten von Ereignissen und Nachrichten an die Anwendung

Voraussetzungen

Wenn Sie kein Azure-Abonnement haben, erstellen Sie ein kostenloses Azure-Konto, bevor Sie beginnen.

Anmelden bei Azure

Melden Sie sich unter https://portal.azure.com/ mit Ihrem Azure-Konto beim Azure-Portal an.

Erstellen einer Azure Web PubSub-Dienstinstanz

Ihre Anwendung stellt eine Verbindung mit einer Instanz des Web PubSub-Diensts in Azure her.

  1. Klicken Sie in der linken oberen Ecke des Azure-Portals auf die Schaltfläche „Neu“. Geben Sie im Bildschirm „Neu“ den Begriff Web PubSub in das Suchfeld ein, und drücken Sie die EINGABETASTE. (Sie können Azure Web PubSub auch in der Kategorie Web durchsuchen.)

    Screenshot: Durchsuchen von Azure Web PubSub im Portal.

  2. Wählen Sie in den Suchergebnissen Web PubSub und dann Erstellen aus.

  3. Geben Sie folgende Einstellungen ein.

    Einstellung Vorgeschlagener Wert BESCHREIBUNG
    Ressourcenname Global eindeutiger Name Der global eindeutige Name, welcher Ihre neue Web PubSub-Dienstinstanz identifiziert. Gültige Zeichen sind a-z, A-Z, 0-9 und -.
    Abonnement Ihr Abonnement Das Azure-Web PubSubAbonnement, unter dem diese neue Web PubSub-Dienstinstanz erstellt wird.
    Ressourcengruppe myResourceGroup Name für die neue Ressourcengruppe, in der Ihre Web PubSub-Dienstinstanz erstellt werden soll
    Location USA (Westen) Wählen Sie eine Region in Ihrer Nähe aus.
    Preisstufe Kostenlos Sie können Azure Web PubSub zuerst kostenlos ausprobieren. Erfahren Sie mehr über die Tarife des Azure Web PubSub-Diensts.
    Einheitenanzahl - Die Einheitenanzahl gibt an, wie viele Verbindungen Ihre Instanz des Web PubSub-Dienstes akzeptieren kann. Jede Einheit unterstützt höchstens 1.000 gleichzeitige Verbindungen. Dies kann nur im Tarif „Standard“ konfiguriert werden.

    Screenshot: Erstellen der Azure Web PubSub-Instanz im Portal.

  4. Wählen Sie Erstellen aus, um mit der Bereitstellung der Instanz des Web PubSub-Dienstes zu beginnen.

Erstellen der Funktionen

  1. Stellen Sie sicher, dass Azure Functions Core Tools installiert ist. Erstellen Sie dann ein leeres Verzeichnis für das Projekt. Führen Sie den Befehl unter diesem Arbeitsverzeichnis aus.

    func init --worker-runtime javascript --model V4
    
  2. Installieren von Microsoft.Azure.WebJobs.Extensions.WebPubSub.

    Bestätigen und aktualisieren Sie host.json's extensionBundle auf Version 4.* oder höher, um Web PubSub-Unterstützung zu erhalten.

    {
      "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[4.*, 5.0.0)"
      }
    }
    
  3. Erstellen Sie eine index-Funktion zum Lesen und Hosten einer statischen Webseite für Clients.

    func new -n index -t HttpTrigger
    
    • Aktualisieren Sie src/functions/index.js und kopieren Sie den folgenden Code.
      const { app } = require('@azure/functions');
      const { readFile } = require('fs/promises');
      
      app.http('index', {
          methods: ['GET', 'POST'],
          authLevel: 'anonymous',
          handler: async (context) => {
              const content = await readFile('index.html', 'utf8', (err, data) => {
                  if (err) {
                      context.err(err)
                      return
                  }
              });
      
              return { 
                  status: 200,
                  headers: { 
                      'Content-Type': 'text/html'
                  }, 
                  body: content, 
              };
          }
      });
      
  4. Erstellen Sie eine negotiate-Funktion, damit Clients die Dienstverbindungs-URL mit Zugriffstoken abrufen können.

    func new -n negotiate -t HttpTrigger
    

    Hinweis

    In diesem Beispiel wird der Microsoft Entra ID-Header der Benutzeridentität x-ms-client-principal-name zum Abrufen von userId verwendet. Dies funktioniert in einer lokalen Funktion nicht. Sie können ihn leer lassen oder auf andere Weise ändern, um userId abzurufen oder zu generieren, wenn Sie lokal agieren. Lassen Sie den Client z. B. einen Benutzernamen eingeben und ihn in einer Abfrage wie ?user={$username} übergeben, wenn Sie die negotiate-Funktion aufrufen, um die URL für die Verbindung mit dem Dienst zu erhalten. Legen Sie in der negotiate-Funktion userId auf den Wert {query.user} fest.

    • Aktualisieren Sie src/functions/negotiate und kopieren Sie den folgenden Code.
      const { app, input } = require('@azure/functions');
      
      const connection = input.generic({
          type: 'webPubSubConnection',
          name: 'connection',
          userId: '{headers.x-ms-client-principal-name}',
          hub: 'simplechat'
      });
      
      app.http('negotiate', {
          methods: ['GET', 'POST'],
          authLevel: 'anonymous',
          extraInputs: [connection],
          handler: async (request, context) => {
              return { body: JSON.stringify(context.extraInputs.get('connection')) };
          },
      });
      
  5. Erstellen Sie eine message-Funktion, um Clientnachrichten über den Dienst zu übertragen.

    func new -n message -t HttpTrigger
    
    • Aktualisieren Sie src/functions/message.js und kopieren Sie den folgenden Code.
      const { app, output, trigger } = require('@azure/functions');
      
      const wpsMsg = output.generic({
          type: 'webPubSub',
          name: 'actions',
          hub: 'simplechat',
      });
      
      const wpsTrigger = trigger.generic({
          type: 'webPubSubTrigger',
          name: 'request',
          hub: 'simplechat',
          eventName: 'message',
          eventType: 'user'
      });
      
      app.generic('message', {
          trigger: wpsTrigger,
          extraOutputs: [wpsMsg],
          handler: async (request, context) => {
              context.extraOutputs.set(wpsMsg, [{
                  "actionName": "sendToAll",
                  "data": `[${context.triggerMetadata.connectionContext.userId}] ${request.data}`,
                  "dataType": request.dataType
              }]);
      
              return {
                  data: "[SYSTEM] ack.",
                  dataType: "text",
              };
          }
      });
      
  6. Fügen Sie im Stammordner des Projekts die Client-Einzelseite index.html hinzu und kopieren Sie den Inhalt.

    <html>
      <body>
        <h1>Azure Web PubSub Serverless Chat App</h1>
        <div id="login"></div>
        <p></p>
        <input id="message" placeholder="Type to chat..." />
        <div id="messages"></div>
        <script>
          (async function () {
            let authenticated = window.location.href.includes(
              "?authenticated=true"
            );
            if (!authenticated) {
              // auth
              let login = document.querySelector("#login");
              let link = document.createElement("a");
              link.href = `${window.location.origin}/.auth/login/aad?post_login_redirect_url=/api/index?authenticated=true`;
              link.text = "login";
              login.appendChild(link);
            } else {
              // negotiate
              let messages = document.querySelector("#messages");
              let res = await fetch(`${window.location.origin}/api/negotiate`, {
                credentials: "include",
              });
              let url = await res.json();
              // connect
              let ws = new WebSocket(url.url);
              ws.onopen = () => console.log("connected");
              ws.onmessage = (event) => {
                let m = document.createElement("p");
                m.innerText = event.data;
                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>
    

Erstellen und Bereitstellen der Azure-Funktions-App

Zum Bereitstellen Ihres Funktionscodes in Azure müssen Sie drei Ressourcen erstellen:

  • Eine Ressourcengruppe als logischen Container für verwandte Ressourcen.
  • Ein Speicherkonto, das verwendet wird, um den Status und andere Informationen zu Ihren Funktionen zu verwalten.
  • Eine Funktions-App, die als Umgebung zum Ausführen Ihres Funktionscodes dient. Eine Funktions-App ist Ihrem lokalen Funktionsprojekt zugeordnet und ermöglicht Ihnen das Gruppieren von Funktionen als logische Einheit, um die Verwaltung, Bereitstellung und Freigabe von Ressourcen zu vereinfachen.

Verwenden Sie die folgenden Befehle, um diese Elemente zu erstellen.

  1. Melden Sie sich bei Azure an, falls dies noch nicht geschehen ist:

    az login
    
  2. Erstellen Sie eine Ressourcengruppe oder verwenden Sie einfach die Ressourcengruppe des Azure Web PubSub-Dienstes weiter:

    az group create -n WebPubSubFunction -l <REGION>
    
  3. Erstellen Sie in Ihrer Ressourcengruppe und Region ein universelles Speicherkonto:

    az storage account create -n <STORAGE_NAME> -l <REGION> -g WebPubSubFunction
    
  4. Erstellen Sie die Funktions-App in Azure:

    az functionapp create --resource-group WebPubSubFunction --consumption-plan-location <REGION> --runtime node --runtime-version 18 --functions-version 4 --name <FUNCIONAPP_NAME> --storage-account <STORAGE_NAME>
    

    Hinweis

    Lesen Sie die Dokumentation zu den Azure Functions Runtime-Versionen, um den Parameter --runtime-version auf einen unterstützten Wert zu setzen.

  5. Stellen Sie das Funktionsprojekts in Azure bereit:

    Nachdem Sie Ihre Funktions-App in Azure erfolgreich erstellt haben, können Sie nun Ihr lokales Funktionsprojekt mit dem Befehl func azure functionapp publish bereitstellen.

    func azure functionapp publish <FUNCIONAPP_NAME>
    
  6. Konfigurieren Sie WebPubSubConnectionString für die Funktions-App:

    Suchen Sie zuerst Ihre Web PubSub-Ressource im Azure-Portal, und kopieren Sie die Verbindungszeichenfolge unter Schlüssel. Navigieren Sie anschließend im Azure-Portal unter Einstellungen>Konfiguration zu den Einstellungen für die Funktions-App. Fügen Sie unter Anwendungseinstellungen ein neues Element mit dem Namen und dem Wert WebPubSubConnectionString ihrer Web PubSub-Ressourcenverbindungszeichenfolge hinzu.

Konfigurieren des Web PubSub-Diensts Event Handler

In diesem Beispiel verwenden Sie WebPubSubTrigger, um auf Anforderungen des Diensts für Upstreams zu lauschen. Web PubSub muss also die Endpunktinformationen der Funktion kennen, um die Clientanforderungen senden zu können. Die Azure Functions-App benötigt zudem einen Systemschlüssel für Sicherheit bei erweiterungsspezifischen Webhookmethoden. Nachdem wir im vorherigen Schritt die Funktions-App mit message-Funktionen bereitgestellt haben, können wir den Systemschlüssel abrufen.

Wechseln Sie zum Azure-Portal, suchen Sie Ihre Funktions-App-Ressource, und wählen Sie >>>>webpubsub_extension aus. Kopieren Sie den Wert von <APP_KEY>.

Screenshot des Abrufens der Systemschlüssel der Funktion.

Legen Sie Event Handler im Azure Web PubSub-Dienst fest. Navigieren Sie im Azure-Portal zu Ihrer Web PubSub-Ressource, und wählen Sie > aus. Fügen Sie eine neue Hubeinstellungszuordnung für die einzelne verwendete Funktion hinzu. Ersetzen Sie die <FUNCTIONAPP_NAME> und <APP_KEY> durch Ihre Werte.

  • Hub-Name: simplechat
  • URL-Vorlage: https://<FUNCTIONAPP_NAME>.azurewebsites.net/runtime/webhooks/webpubsub?code=<APP_KEY>
  • Benutzerereignismuster: *
  • Systemereignisse: (bei diesem Beispiel keine Konfiguration nötig)

Screenshot: Festlegen des Ereignishandlers

Konfiguration zum Aktivieren der Clientauthentifizierung

Wechseln Sie zum Azure-Portal. Wählen Sie Ihre Funktions-App-Ressource und dann > aus. Klicken Sie auf Add identity provider. Legen Sie die Einstellungen für die App Service-Authentifizierung auf Nicht authentifizierten Zugriff zulassen fest, damit Ihre Client-Indexseite von anonymen Benutzern besucht werden kann, bevor sie zur Authentifizierung weitergeleitet werden. Speichern Sie anschließend.

Hier wählen wir Microsoft als Bezeichner des Anbieters, der x-ms-client-principal-name als userId in der negotiate-Funktion verwendet. Außerdem können Sie andere Identitätsanbieter konfigurieren, indem Sie den Links folgen. Vergessen Sie dabei nicht, den userId-Wert in der negotiate-Funktion entsprechend zu aktualisieren.

Testen der Anwendung

Nun können Sie Ihre Seite in Ihrer Funktions-App https://<FUNCTIONAPP_NAME>.azurewebsites.net/api/index testen. Siehe die Momentaufnahme.

  1. Klicken Sie auf login, um sich zu authentifizieren.
  2. Geben Sie die Nachricht in das Eingabefeld ein, um zu chatten.

In der Nachrichtenfunktion senden wir die Nachricht des Anrufers an alle Clients und geben den Anrufer mit der Nachricht [SYSTEM] ack zurück. In der Momentaufnahme des Beispielchats sind also die ersten vier Nachrichten vom aktuellen Client und die letzten beiden Nachrichten von einem anderen Client.

Screenshot des Chatbeispiels.

Bereinigen von Ressourcen

Wenn Sie diese App nicht mehr benötigen, löschen Sie alle im Rahmen dieser Dokumentation erstellten Ressourcen, damit keine Gebühren anfallen. Gehen Sie dazu wie folgt vor:

  1. Klicken Sie ganz links im Azure-Portal auf Ressourcengruppen und anschließend auf die erstellte Ressourcengruppe. Sie können auch das Suchfeld verwenden, um nach dem Namen der Ressourcengruppe zu suchen.

  2. Wählen im daraufhin angezeigten Fenster die Ressourcengruppe und anschließend die Option Ressourcengruppe löschen aus.

  3. Geben Sie im neuen Fenster den Namen der Ressourcengruppe ein, die Sie löschen möchten, und wählen Sie Löschen aus.

Nächste Schritte

In dieser Schnellstartanleitung haben Sie gelernt, wie Sie eine serverlose Benachrichtigungsanwendung ausführen. Sie können nun damit beginnen, Ihre eigene Anwendung zu erstellen.