Samouczek: Zbuduj prostą aplikację czatu z pomocą chat-huba

W tym tutorialu budujesz prosty przepływ czatu w czasie rzeczywistym, korzystając z chatu Web PubSub.

Ty:

  • Ustaw serwer backendowy do wydawania adresów dostępu klienta
  • Połącz klientów z chatem Web PubSub
  • Utwórz pokój
  • Wysyłanie i odbieranie komunikatów
  • Zarządzaj członkostwem w pokoju

Na koniec masz działające doświadczenie czatu wspieranego przez Azure Web PubSub.

Wymagania wstępne

  • Subskrypcja platformy Azure
  • Node.js 18 lub nowsza wersja

Utwórz zasób Web PubSub z centrum czatu

Stwórz zasób Azure Web PubSub i skonfiguruj hub czatu o nazwie demo-chat.

Instalowanie zależności

Zależności serwera

npm install express @azure/web-pubsub @azure/web-pubsub-express

Zależności klientów

npm install @azure/web-pubsub-chat-client

Krok 1: Stwórz serwer backend

Serwer backend odpowiada za:

  • Uwierzytelnianie użytkowników
  • Wysyłanie adresów URL dostępu klienta

Kod serwera

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

const hubName = 'demo-chat';
const port = process.env.PORT || 3000;

const connectionString = process.env.WEB_PUBSUB_CONNECTION_STRING;
if (!connectionString) {
  throw new Error('WEB_PUBSUB_CONNECTION_STRING is not set');
}

const app = express();

const serviceClient = new WebPubSubServiceClient(
  connectionString,
  hubName,
  { allowInsecureConnection: true }
);

Dlaczego ten krok istnieje

Chociaż usługa Web PubSub obsługuje anonimowe połączenia, najczęściej stosowany jest model dostępu wydawany przez serwer.

Twój serwer generuje ograniczony czasowo adres URL dostępu klienta , który koduje tożsamość użytkownika i uprawnienia. Takie podejście:

  • Zabezpiecza dane uwierzytelniające
  • Pozwala Twojej aplikacji kontrolować uwierzytelnianie i autoryzację
  • Zgodna z oczekiwaniami dotyczącymi bezpieczeństwa przedsiębiorstwa

Krok 2: Dodaj punkt końcowy negocjacji

Punkt końcowy negocjacji zwraca adres URL dostępu klienta, którego klienci czatu używają do połączenia.

app.get('/negotiate', async (req, res) => {
  console.log(`received negotiate request: ${JSON.stringify(req.query)}`);

  const userId = req.query.userId;
  if (!userId) {
    return res.status(400).send('Missing userId');
  }

  const token = await serviceClient.getClientAccessToken({
    userId,
  });

  res.json({
    url: token.url,
  });
});

Dlaczego ten krok istnieje

Klienci czatu muszą łączyć się jako konkretny użytkownik.

Punkt końcowy negocjacji to miejsce, gdzie Twoja aplikacja:

  • Przypisuje tożsamość na poziomie aplikacji do użytkownika czatu
  • Wydaje ograniczony, tymczasowy adres URL dostępu
  • Decyduje, kto może nawiązać połączenie

W produkcji ten punkt końcowy zazwyczaj następuje:

  • Weryfikuje uwierzytelnianie (ciasteczka, nagłówki, tokeny)
  • Egzekwuje zasady autoryzacji

Krok 3: Uruchom serwer

app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
});

Twój backend jest teraz gotowy do przyjmowania połączeń z klientami.

Krok 4: Połącz klientów z chatem hub

Na kliencie pobieraj adresy URL dostępu z serwera i loguj się do huba demo-chat .

import { ChatClient } from '@azure/web-pubsub-chat-client';

// Fetch a fresh client access URL from the negotiate endpoint.
const getClientAccessUrl = (userId) =>
  fetch(`/negotiate?userId=${userId}`)
    .then(r => r.json())
    .then(d => d.url);

// Option 1: start with a one-time client access URL.
const alice = await ChatClient.start(await getClientAccessUrl('alice'));
console.log(`Started as: ${alice.userId}`);

// Option 2: start with a credential so the client can refresh the URL itself.
const charlie = await ChatClient.start({
  getClientAccessUrl: () => getClientAccessUrl('charlie'),
});
console.log(`Started as: ${charlie.userId}`);

Dlaczego ten krok istnieje

Hub czatu Web PubSub opiera się na modelu połączenia Web PubSub. Ten krok uwierzytelniania:

  • Ustanawia połączenie w czasie rzeczywistym
  • Kojarzy połączenie z tożsamością użytkownika
  • Umożliwia funkcje specyficzne dla czatu, takie jak pokoje i historia wiadomości

Krok 5: Słuchaj wydarzeń czatowych

Rejestruj słuchaczy, aby otrzymywać aktualizacje w czasie rzeczywistym.

alice.on('message', (event) => {
  const msg = event.message;
  console.log(`Alice received: ${msg.createdBy}: ${msg.content.text}`);
});

alice.on('room-joined', (event) => {
  console.log(`Alice joined room: ${event.room.title}`);
});

charlie.on('message', (event) => {
  const msg = event.message;
  console.log(`Charlie received: ${msg.createdBy}: ${msg.content.text}`);
});

charlie.on('room-joined', (event) => {
  console.log(`Charlie joined room: ${event.room.title}`);
});

Klient czatu to emiter zdarzeń. Oprócz message i room-joined, można słuchać room-left, member-joined, member-left, started, oraz stopped. Używam off z tymi samymi argumentami, aby usunąć słuchacza.

Dlaczego ten krok istnieje

Czat jest z natury napędzany wydarzeniami. Ci słuchacze pozwalają Twojej aplikacji na:

  • Reaguj na nadchodzące wiadomości
  • Aktualizuj interfejs użytkownika, gdy użytkownicy dołączają do pokoi
  • Zostań zsynchronizowany na wielu urządzeniach lub zakładkach przeglądarki

Krok 6: Stwórz pokój i wyślij wiadomości

Stwórz pokój i dodaj początkowych członków:

const room = await alice.createRoom('My Room', ['charlie']);

Wyślij wiadomość do pokoju:

await alice.sendToRoom(room.roomId, 'Hello!');

Wiadomości są dostarczane w czasie rzeczywistym do wszystkich członków sali.

Dlaczego ten krok istnieje

Pokoje zapewniają strukturę do czatu:

  • To oni definiują, kto otrzymuje wiadomości.
  • Utrzymują historię wiadomości.
  • Pozwalają na skalowanie czatu poza komunikację jeden do jednego.

Krok 7: Pobierz historię wiadomości

Pobierz wcześniejsze wiadomości z pokoju. listRoomMessages zwraca stronicowany iterator asynchroniczny, dzięki czemu możesz bezpośrednio iterować po wszystkich wiadomościach:

for await (const msg of alice.listRoomMessages(room.roomId)) {
  console.log(`${msg.createdBy}: ${msg.content.text}`);
}

Albo ładuj historię strona po stronie (na przykład „załaduj 50, potem kolejne 50 podczas przewijania w górę”):

const pages = alice.listRoomMessages(room.roomId).byPage({ maxPageSize: 50 });
const firstPage = await pages.next();
const messages = firstPage.value ?? [];

Dlaczego ten krok istnieje

Nowo połączeni klienci często potrzebują kontekstu.

Historia wiadomości pozwala Twojej aplikacji:

  • Renderowanie istniejących komunikatów
  • Wznowij czat po ponownym połączeniu
  • Obsługa wielu urządzeń

Krok 8: Zarządzaj członkami sali

Dodaj użytkownika do pokoju:

await alice.addUserToRoom(room.roomId, 'bob');

Usuń użytkownika z pokoju:

await alice.removeUserFromRoom(room.roomId, 'bob');

Zmiany członkostwa wchodzą w życie natychmiast.

Krok 9: Posprzątaj

Gdy klient nie musi już otrzymywać wiadomości:

await alice.stop();
await charlie.stop();

Co utworzono

W ramach tego szybkiego przewodnika wykonasz następujące czynności:

  • Wydano bezpieczne adresy URL dostępu klienta z serwera
  • Klienci połączeni z centrum czatu
  • Utworzone i dołączone pokoje czatu
  • Wysyłane i odbierane wiadomości w czasie rzeczywistym
  • Wczytana historia wiadomości
  • Członkostwo w zarządzanych pokojach

Wszystko to bez zarządzania serwerami WebSocket, logiką rozprzestrzeniania się czy utrzymywania wiadomości.