Tutoriel : Publication de messages et abonnement entre clients WebSocket avec le sous-protocole

Dans le tutoriel Créer une application de conversation, vous avez appris à utiliser les API WebSocket pour envoyer et recevoir des données avec Azure Web PubSub. Comme vous pouvez le constater, aucun protocole n’est nécessaire lorsque le client communique avec le service. Par exemple, vous pouvez envoyer n’importe quel type de données à l’aide WebSocket.send()du serveur, et le serveur le reçoit tout comme il l’est. Le processus d’API WebSocket est facile à utiliser, mais la fonctionnalité est limitée. Par exemple, vous ne pouvez pas spécifier le nom de l’événement lors de l’envoi de l’événement à votre serveur, ou publier un message auprès d’autres clients au lieu de l’envoyer à votre serveur. Dans ce tutoriel, vous allez apprendre à utiliser le sous-protocole pour étendre les fonctionnalités du client.

Dans ce tutoriel, vous allez apprendre à :

  • Créer une instance de service Azure Web PubSub
  • Générer l’URL complète pour établir la connexion WebSocket
  • Publier des messages entre des clients WebSocket avec le sous-protocole

Si vous n’avez pas d’abonnement Azure, créez un compte gratuit Azure avant de commencer.

Prérequis

  • La version 2.22.0 (ou une version ultérieure) d’Azure CLI est requise pour cette configuration. Si vous utilisez Azure Cloud Shell, la version la plus récente est déjà installée.

Création d’une instance Azure Web PubSub

Créer un groupe de ressources

Un groupe de ressources est un conteneur logique dans lequel les ressources Azure sont déployées et gérées. Utilisez la commande az group create pour créer un groupe de ressources nommé myResourceGroup à l’emplacement eastus.

az group create --name myResourceGroup --location EastUS

Créer une instance Web PubSub

Exécutez la commande az extension add pour installer ou mettre à niveau l’extension webpubsub vers la version actuelle.

az extension add --upgrade --name webpubsub

Utilisez la commande az webpubsub create d’Azure CLI pour créer une instance Web PubSub dans le groupe de ressources que vous avez créé. La commande suivante crée une ressource Web PubSub GRATUITE sous le groupe de ressources myResourceGroup dans la zone EastUS :

Important

Chaque ressource Web PubSub doit avoir un nom unique. Remplacez <your-unique-keyvault-name> par le nom de votre Web PubSub dans les exemples suivants.

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

La sortie de cette commande affiche les propriétés de la ressource que vous venez de créer. Notez les deux propriétés ci-dessous :

  • Nom de la ressource : nom que vous avez fourni au paramètre --name ci-dessus.
  • Nom d’hôte : dans l’exemple, le nom d’hôte est <your-unique-resource-name>.webpubsub.azure.com/.

À ce stade, votre compte Azure est le seul autorisé à effectuer des opérations sur cette nouvelle ressource.

Récupération de la chaîne de connexion pour une utilisation ultérieure

Important

Une chaîne de connexion contient les informations d’autorisation requises pour que votre application accède au service Azure Web PubSub. La clé d’accès à l’intérieur dans la chaîne de connexion est semblable à un mot de passe racine pour votre service. Veillez toujours à protéger vos clés d’accès dans les environnements de production. Utilisez Azure Key Vault pour gérer et effectuer la rotation de vos clés en toute sécurité. Évitez de distribuer des clés d’accès à d’autres utilisateurs, de les coder en dur ou de les enregistrer en texte brut dans un emplacement accessible à d’autres personnes. Effectuez une rotation de vos clés si vous pensez qu’elles ont pu être compromises.

Utilisez la commande Azure CLI az webpubsub key pour obtenir la valeur ConnectionString du service. Remplacez l’espace réservé <your-unique-resource-name> par le nom de votre instance Azure Web PubSub.

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

Copiez la chaîne de connexion à utiliser plus tard.

Copiez la Connecter ionString extraite et utilisez plus loin dans ce didacticiel comme valeur <connection_string>.

Configuration du projet

Prérequis

Utilisation d’un sous-protocole

Le client peut lancer une connexion WebSocket avec un sous-protocole spécifique. Le service Azure Web PubSub prend en charge un sous-protocole appelé json.webpubsub.azure.v1, qui permet aux clients de publier/s’abonner directement via le service Web PubSub plutôt que d’avoir à effectuer un aller-retour au serveur en amont. Pour plus d’informations sur ce sous-protocole, consultez Sous-protocole JSON WebSocket pris en charge par Azure Web PubSub.

Tout autre nom de protocole utilisé est ignoré par le service et transmis au serveur dans le gestionnaire d’événements de connexion. Vous pouvez ainsi créer vos propres protocoles.

Créons maintenant une application web avec le sous-protocole json.webpubsub.azure.v1.

  1. Installer des dépendances

    mkdir logstream
    cd logstream
    dotnet new web
    dotnet add package Microsoft.Extensions.Azure
    dotnet add package Azure.Messaging.WebPubSub
    
  2. Créez le côté serveur pour héberger l’API et la page web /negotiate.

    Mettez à jour Program.cs avec le code suivant.

    • Utilisez AddAzureClients pour ajouter le service client et lire la chaîne de connexion à partir de la configuration.
    • Ajoutez app.UseStaticFiles(); avant app.Run(); pour prendre en charge les fichiers statiques.
    • Enfin, mettez à jour app.MapGet pour générer le jeton d’accès client avec des demandes /negotiate.
    using Azure.Messaging.WebPubSub;
    using Microsoft.Extensions.Azure;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddAzureClients(s =>
    {
        s.AddWebPubSubServiceClient(builder.Configuration["Azure:WebPubSub:ConnectionString"], "stream");
    });
    
    var app = builder.Build();
    app.UseStaticFiles();
    app.MapGet("/negotiate", async context =>
    {
        var service = context.RequestServices.GetRequiredService<WebPubSubServiceClient>();
        var response = new
        {
            url = service.GetClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" }).AbsoluteUri
        };
        await context.Response.WriteAsJsonAsync(response);
    });
    
    app.Run();
    
  3. Créez la page web.

    Créez une page HTML comportant le contenu suivant et enregistrez-la sous wwwroot/index.html :

    <html>
      <body>
        <div id="output"></div>
        <script>
          (async function () {
            let res = await fetch('/negotiate')
            let data = await res.json();
            let ws = new WebSocket(data.url, 'json.webpubsub.azure.v1');
            ws.onopen = () => {
              console.log('connected');
            };
    
            let output = document.querySelector('#output');
            ws.onmessage = event => {
              let d = document.createElement('p');
              d.innerText = event.data;
              output.appendChild(d);
            };
          })();
        </script>
      </body>
    </html>                                                                
    

    Le code ci-dessus se connecte au service et imprime tous les messages reçus sur la page. La principale modification est que le sous-protocole est spécifié lors de la création de la connexion WebSocket.

  4. Exécutez le serveur.

    Nous utilisons l’outil Secret Manager pour .NET Core afin de définir la chaîne de connexion. Exécutez la commande suivante, en remplaçant <connection_string> par la chaîne récupérée à l’étape précédente, et ouvrez http://localhost:5000/index.html dans un navigateur :

    dotnet user-secrets init
    dotnet user-secrets set Azure:WebPubSub:ConnectionString "<connection-string>"
    dotnet run
    

    Si vous utilisez Chrome, vous pouvez appuyer sur F12 ou cliquer avec le bouton droit sur ->Inspect ->Developer Tools, puis sélectionner l’onglet Réseau . Chargez la page web et vous pouvez voir que la connexion WebSocket est établie. Sélectionnez cette option pour inspecter la connexion WebSocket. Vous pouvez voir le message d’événement ci-dessous connected reçu dans le client. Vous pouvez obtenir le connectionId généré pour ce client.

    {"type":"system","event":"connected","userId":null,"connectionId":"<the_connection_id>"}
    

Avec l’aide du sous-protocole, vous pouvez donc obtenir certaines métadonnées de la connexion lorsqu’elle est connected.

Le client reçoit désormais un message JSON au lieu d’un texte brut. Le message JSON contient plus d’informations telles que le type et la source du message. Vous pouvez utiliser ces informations pour effectuer un traitement plus poussé du message (par l’exemple l’afficher dans un autre style s’il provient d’une source différente), que vous trouverez dans les sections suivantes.

Publication de messages provenant du client

Dans le tutoriel Créer une application de conversation, lorsque le client envoie un message via une connexion WebSocket au service Web PubSub, le service déclenche un événement utilisateur côté serveur. Avec le sous-protocole, le client a plus de fonctionnalités en envoyant un message JSON. Par exemple, vous pouvez publier des messages directement à partir du client via le service Web PubSub sur d’autres clients.

Cela est utile si vous souhaitez diffuser en temps réel une grande quantité de données vers d’autres clients. Utilisons-la pour créer une application de streaming de journaux, qui peut transmettre en temps réel les journaux de la console au navigateur.

  1. Création du programme de streaming

    Créez un programme stream :

    mkdir stream
    cd stream
    dotnet new console
    

    Mettez à jour Program.cs avec le contenu suivant :

    using System;
    using System.Net.Http;
    using System.Net.WebSockets;
    using System.Text;
    using System.Text.Json;
    using System.Threading.Tasks;
    
    namespace stream
    {
        class Program
        {
            private static readonly HttpClient http = new HttpClient();
            static async Task Main(string[] args)
            {
                // Get client url from remote
                var stream = await http.GetStreamAsync("http://localhost:5000/negotiate");
                var url = (await JsonSerializer.DeserializeAsync<ClientToken>(stream)).url;
                var client = new ClientWebSocket();
                client.Options.AddSubProtocol("json.webpubsub.azure.v1");
    
                await client.ConnectAsync(new Uri(url), default);
    
                Console.WriteLine("Connected.");
                var streaming = Console.ReadLine();
                while (streaming != null)
                {
                    if (!string.IsNullOrEmpty(streaming))
                    {
                        var message = JsonSerializer.Serialize(new
                        {
                            type = "sendToGroup",
                            group = "stream",
                            data = streaming + Environment.NewLine,
                        });
                        Console.WriteLine("Sending " + message);
                        await client.SendAsync(Encoding.UTF8.GetBytes(message), WebSocketMessageType.Text, true, default);
                    }
    
                    streaming = Console.ReadLine();
                }
    
                await client.CloseAsync(WebSocketCloseStatus.NormalClosure, null, default);
            }
    
            private sealed class ClientToken
            {
                public string url { get; set; }
            }
        }
    }
    
    

    Vous pouvez voir qu’il y a un nouveau concept de « groupe » ici. Ce concept logique permet, dans un hub, de publier un message sur un groupe de connexions. Un hub peut comporter plusieurs groupes, et un client a la possibilité de s’abonner à plusieurs groupes en même temps. Lorsque vous utilisez le sous-protocole, vous pouvez publier un message sur un groupe seulement au lieu de le diffuser sur l’ensemble du hub. Pour plus d’informations sur les termes, consultez les concepts de base.

  2. Dans la mesure où nous utilisons un groupe, nous devons également mettre à jour la page web index.html pour joindre le groupe lorsque la connexion WebSocket est établie dans le rappel ws.onopen.

    let ackId = 0;
    ws.onopen = () => {
      console.log('connected');
      ws.send(JSON.stringify({
        type: 'joinGroup',
        group: 'stream',
        ackId: ++ackId
      }));
    };
    

    Comme vous pouvez le constater, le client rejoint le groupe en envoyant un message dans le type joinGroup.

  3. De même, mettez légèrement à jour la logique de rappel ws.onmessage pour analyser la réponse JSON et imprimer uniquement les messages provenant du groupe stream. Elle fait ainsi office d’imprimante de stream en direct.

    ws.onmessage = event => {
      let message = JSON.parse(event.data);
      if (message.type === 'message' && message.group === 'stream') {
        let d = document.createElement('span');
        d.innerText = message.data;
        output.appendChild(d);
        window.scrollTo(0, document.body.scrollHeight);
      }
    };
    
  4. Dans un souci de sécurité, un client ne peut par défaut pas publier sur un groupe ni s’y abonner par lui-même. Vous avez donc remarqué que nous avons défini roles sur le client lors de la génération du jeton :

    Définissez roles quand GenerateClientAccessUri dans Startup.cs :

    service.GenerateClientAccessUri(roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" })
    
  5. Enfin, appliquez également un style à index.html pour qu’il s’affiche correctement.

    <html>
    
      <head>
        <style>
          #output {
            white-space: pre;
            font-family: monospace;
          }
        </style>
      </head>
    

Exécutez maintenant le code ci-dessous et tapez n’importe quel texte et ils sont affichés dans le navigateur en temps réel :

ls -R | dotnet run

# Or call `dir /s /b | dotnet run` when you are using CMD under Windows

Vous pouvez également le ralentir pour voir que les données sont diffusées en temps réel dans le navigateur :

for i in $(ls -R); do echo $i; sleep 0.1; done | dotnet run

L’exemple de code complet de ce tutoriel est disponible ici.

Étapes suivantes

Ce tutoriel vous fournit une idée de base de la connexion au service Web PubSub et de la façon de publier des messages sur les clients connectés à l’aide du sous-protocole.

Pour plus d’informations sur l’utilisation du service, consultez les autres tutoriels.