Azure Web PubSub CloudEvents-Handler für Express

Der Azure Web PubSub-Dienst ist ein von Azure verwalteter Dienst, mit dem Entwickler problemlos Webanwendungen mit Echtzeitfeatures und Veröffentlichungsabonnentmustern erstellen können. Jedes Szenario, das Echtzeit-Publish-Subscribe-Messaging zwischen Server und Clients oder zwischen Clients erfordert, kann den Azure Web PubSub-Dienst verwenden. Herkömmliche Echtzeitfeatures, die häufig eine Abfrage vom Server oder das Senden von HTTP-Anforderungen erfordern, können auch den Azure Web PubSub-Dienst verwenden.

Wenn eine WebSocket-Verbindung eine Verbindung herstellt, transformiert der Web PubSub-Dienst den Verbindungslebenszyklus und Nachrichten in Ereignisse im CloudEvents-Format. Diese Bibliothek bietet eine Express-Middleware zum Behandeln von Ereignissen, die den Lebenszyklus und die Nachrichten der WebSocket-Verbindung darstellen, wie im folgenden Diagramm dargestellt:

cloudevents

Details zu den hier verwendeten Begriffen sind im Abschnitt Schlüsselkonzepte beschrieben.

Quellcode | Paket (NPM) | API-Referenzdokumentation | Produktdokumentation | Proben

Erste Schritte

Derzeit unterstützte Umgebungen

Voraussetzungen

1. Installieren des Pakets @azure/web-pubsub-express

npm install @azure/web-pubsub-express

2. Erstellen eines WebPubSubEventHandler

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat");

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Zentrale Konzepte

Verbindung

Eine Verbindung, auch bekannt als Client oder Client-Verbindung, stellt eine einzelne WebSocket-Verbindung dar, die mit dem Web PubSub-Dienst verbunden ist. Bei erfolgreicher Verbindung wird dieser Verbindung vom Web PubSub-Dienst eine eindeutige Verbindungs-ID zugewiesen.

Drehscheibe

Ein Hub ist ein logisches Konzept für eine Reihe von Clientverbindungen. Normalerweise nutzt man einen Hub für einen bestimmten Zweck, zum Beispiel einen Chat-Hub oder einen Benachrichtigungshub. Wenn eine Clientverbindung erstellt wird, wird eine Verbindung mit einem Hub hergestellt, und während der Lebensdauer gehört sie zu diesem Hub. Von verschiedenen Anwendungen können unterschiedliche Hubnamen verwendet werden, um gemeinsam einen einzelnen Azure Web PubSub-Dienst zu nutzen.

Gruppe

Eine Gruppe ist eine Teilmenge von Verbindungen mit dem Hub. Sie können einer Gruppe eine Clientverbindung hinzufügen und sie jederzeit wieder aus der Gruppe entfernen. Beispiel: Wenn ein Client einem Chatroom beitritt oder wenn ein Client den Chatroom verlässt, kann dieser Chatroom als Gruppe betrachtet werden. Ein Client kann mehreren Gruppen beitreten, und eine Gruppe kann mehrere Clients enthalten.

User

Verbindungen mit Web PubSub können einem Benutzer angehören. Ein Benutzer kann über mehrere Verbindungen verfügen, etwa, wenn ein einzelner Benutzer über mehrere Geräte oder mehrere Browsertabs verbunden ist.

Clientereignisse

Ereignisse werden während des Lebenszyklus einer Client-Verbindung erstellt. Zum Beispiel erzeugt eine einfache WebSocket-Clientverbindung ein connect Ereignis, wenn er versucht, sich mit dem Dienst zu verbinden, ein connected Ereignis, wenn er erfolgreich mit dem Dienst verbunden ist, ein message Ereignis, wenn er Nachrichten an den Dienst sendet, und ein Ereignis disconnected , wenn er sich vom Dienst trennt.

Ereignishandler

Der Ereignishandler enthält die Logik, um die Client-Events zu handhaben. Der Ereignishandler muss im Dienst im Voraus über das Portal oder die Azure CLI registriert und konfiguriert werden. Der Ort, an dem die Ereignishandler-Logik gehostet wird, wird im Allgemeinen als serverseitig betrachtet.

Beispiele

Bearbeiten Sie die connect Anforderung und weisen Sie <userId> zu

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  handleConnect: (req, res) => {
    // auth the connection and set the userId of the connection
    res.success({
      userId: "<userId>",
    });
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die connect Anfrage und lehne die Verbindung ab, wenn die Authentifizierung fehlschlägt

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  handleConnect: (req, res) => {
    // auth the connection and reject the connection if auth failed
    res.fail(401, "Unauthorized");
    // the following method is also a valid approach
    // res.failWith({ code: 401, detail: "Unauthorized" });
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die connected Anfrage

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  onConnected: (connectedRequest) => {
    // Your onConnected logic goes here
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die onGroupJoined Anfrage

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  onGroupJoined: (groupJoinedRequest) => {
    console.log(
      `Connection ${groupJoinedRequest.context.connectionId} joined group ${groupJoinedRequest.group}`,
    );
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die onGroupLeft Anfrage

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  onGroupLeft: (groupLeftRequest) => {
    console.log(
      `Connection ${groupLeftRequest.context.connectionId} left group ${groupLeftRequest.group}`,
    );
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die onDisconnected Anfrage

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  onDisconnected: (disconnectedRequest) => {
    // Your onDisconnected logic goes here
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die connect Anfrage für mqtt und assign <userId> und <mqtt> Eigenschaften

import { WebPubSubEventHandler, MqttConnectRequest } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  handleConnect: (req, res) => {
    if (req.context.clientProtocol === "mqtt") {
      // return mqtt response when request is of MQTT kind
      // get connect request as mqtt request and print it
      const mqttRequest = req as MqttConnectRequest;
      console.log(mqttRequest);

      // auth the connection and return mqtt response
      res.success({
        userId: "user1",
        mqtt: { userProperties: [{ name: "a", value: "b" }] },
      });
    } else {
      res.success({
        userId: "user1",
      });
    }
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die connect Anfrage für mqtt und lehne die Verbindung ab, falls die Authentifizierung fehlschlug

import { WebPubSubEventHandler, MqttConnectRequest } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  handleConnect: (req, res) => {
    // auth the connection and reject the connection if auth failed
    if (req.context.clientProtocol === "mqtt") {
      // return mqtt error response when request is of MQTT kind
      // get connect request as mqtt request and print it
      const mqttRequest = req as MqttConnectRequest;
      console.log(mqttRequest);

      // auth the connection and return mqtt failure response
      res.fail(401, "Not Authorized");

      // Or use below method for more fine-grained control over the MQTT return code
      // res.failWith({ mqtt: { code: MqttV500ConnectReasonCode.NotAuthorized } });
    } else res.success();
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Behandle die onDisconnected Anfrage für MQTT

import { WebPubSubEventHandler, MqttDisconnectedRequest } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  onDisconnected: (disconnectedRequest) => {
    if (disconnectedRequest.context.clientProtocol === "mqtt") {
      // get disconnect request as mqtt request and print it
      const mqttRequest = disconnectedRequest as MqttDisconnectedRequest;
      console.log(mqttRequest.mqtt);
      // Your onDisconnected logic goes here
    } else {
      console.log(disconnectedRequest);
      // Your onDisconnected logic goes here
    }
  },
  allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Nur angegebene Endpunkte zulassen

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  allowedEndpoints: [
    "https://<yourAllowedService1>.webpubsub.azure.com",
    "https://<yourAllowedService2>.webpubsub.azure.com",
  ],
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Festlegen des benutzerdefinierten Ereignishandlerpfads

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  path: "/customPath1",
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  // Azure WebPubSub Upstream ready at http://localhost:3000/customPath1
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Festlegen und Lesen des Verbindungszustands

import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";

const handler = new WebPubSubEventHandler("chat", {
  handleConnect(req, res) {
    // You can set the state for the connection, it lasts throughout the lifetime of the connection
    res.setState("calledTime", 1);
    res.success();
  },
  handleUserEvent(req, res) {
    const calledTime = req.context.states.calledTime++;
    console.log(calledTime);
    // You can also set the state here
    res.setState("calledTime", calledTime);
    res.success();
  },
});

const app = express();

app.use(handler.getMiddleware());

app.listen(3000, () =>
  console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);

Troubleshooting

Aktivieren von Protokollen

Das Aktivieren der Protokollierung kann hilfreiche Informationen zu Fehlern aufdecken. Um ein Protokoll von HTTP-Anforderungen und -Antworten anzuzeigen, legen Sie die AZURE_LOG_LEVEL Umgebungsvariable auf infofest.

export AZURE_LOG_LEVEL=verbose

Alternativ kann das Logging zur Laufzeit aktiviert werden, indem setLogLevel im @azure/logger aufgerufen wird:

import { setLogLevel } from "@azure/logger";

setLogLevel("info");

Ausführlichere Anweisungen zum Aktivieren von Protokollen finden Sie in den @azure/Logger-Paketdokumenten.

Live-Ablaufverfolgung

Verwenden Sie Live Trace aus dem Web PubSub-Dienstportal, um den Livedatenverkehr anzuzeigen.

Nächste Schritte

Bitte schauen Sie sich das Verzeichnis samples an, um detaillierte Beispiele zur Nutzung dieser Bibliothek zu finden.

Contributing

Wenn Sie zu dieser Bibliothek beitragen möchten, lesen Sie bitte den beitragenden Leitfaden, um mehr darüber zu erfahren, wie man den Code erstellt und testet.