Développement et configuration Azure Functions avec Azure SignalR Service

Les applications Azure Functions peuvent utiliser les liaisons Azure SignalR Service pour ajouter des fonctionnalités en temps réel. Les applications clientes utilisent des SDK clients disponibles dans plusieurs langages pour se connecter à Azure SignalR Service et recevoir des messages en temps réel.

Cet article décrit les concepts du développement et de la configuration d’une application de fonction Azure qui est intégrée à SignalR Service.

Configuration de SignalR Service

Azure SignalR Service peut être configuré dans des modes différents. Quand il est utilisé avec Azure Functions, le service doit être configuré en mode Serverless.

Dans le portail Azure, recherchez la page Paramètres de votre ressource SignalR Service. Définissez le Mode de service sur Serverless.

Mode SignalR Service

Développement Azure Functions

Une application serverless en temps réel intégrée à Azure Functions et Azure SignalR Service nécessite au moins deux fonctions Azure :

  • Une fonction negotiate que le client appelle pour obtenir un jeton d’accès SignalR Service valide et une URL de point de terminaison.
  • Une ou plusieurs fonctions qui gèrent les messages envoyés par SignalR Service aux clients.

Fonction de négociation

Une application cliente nécessite un jeton d’accès valide pour se connecter à Azure SignalR Service. Un jeton d’accès peut être anonyme ou authentifié avec un ID d’utilisateur. Les applications SignalR Service serverless nécessitent un point de terminaison HTTP nommé negotiate pour obtenir un jeton et d’autres informations de connexion, telles que l’URL de point de terminaison de SignalR Service.

Utilisez une fonction Azure déclenchée via HTTP et la liaison d’entrée SignalRConnectionInfo pour générer l’objet d’informations de connexion. La fonction doit avoir une route HTTP qui se termine par /negotiate.

Avec le modèle basé sur la classe en C#, vous n’avez pas besoin de la liaison d’entrée SignalRConnectionInfo et pouvez ajouter des revendications personnalisées beaucoup plus facilement. Pour plus d'informations, consultez Expérience de négociation dans un modèle basé sur les classes.

Pour plus d’informations sur la fonction negotiate, consultez Développement Azure Functions.

Pour savoir comment créer un jeton authentifié, reportez-vous à Utilisation de l’authentification App Service.

Gérer les messages envoyés par SignalR Service

Utilisez la liaison SignalRTrigger pour traiter les messages envoyés par Azure SignalR Service. Vous pouvez être averti lorsque les clients envoient des messages ou lorsqu’ils se connectent ou se déconnectent.

Pour plus d’informations, consultez les informations de référence sur la liaison de déclencheur SignalR Service.

Vous devez également configurer le point de terminaison de votre fonction sous forme de point de terminaison en amont. Ainsi, le service déclenche la fonction lorsqu'un message est envoyé par un client. Pour plus d’informations sur la configuration des points de terminaison en amont, consultez les Points de terminaison en amont.

Remarque

SignalR Service ne prend pas en charge le message StreamInvocation d’un client en mode Serverless.

Envoi de messages et gestion de l’appartenance au groupe

Utilisez la liaison de sortie SignalR pour envoyer des messages aux clients connectés à Azure SignalR Service. Vous pouvez diffuser des messages à tous les clients ou à un sous-ensemble de clients. Par exemple, envoyez uniquement des messages aux clients authentifiés avec un ID d’utilisateur spécifique ou uniquement à un groupe spécifique.

Les utilisateurs peuvent être ajoutés à un ou plusieurs groupes. Vous pouvez également utiliser la liaison de sortie SignalR pour ajouter ou supprimer des utilisateurs dans des groupes.

Pour plus d'informations, consultez la référence de la liaison de sortie SignalR.

Hubs SignalR

SignalR a un concept de hubs. L’étendue de chaque connexion cliente et de chaque message envoyé à partir d’Azure Functions correspond à un hub spécifique. Vous pouvez utiliser des hubs pour organiser vos connexions et messages en espaces de noms logiques.

Modèle basé sur la classe

Le modèle basé sur la classe est dédié à C#.

Le modèle basé sur les classes offre une meilleure expérience de programmation. Celle-ci peut remplacer les liaisons d'entrée et de sortie Azure SignalR Service, avec les caractéristiques suivantes :

  • Expérience plus souple en matière de négociation, d'envoi de messages et de gestion de groupes.
  • D'autres fonctionnalités de gestion sont prises en charge, notamment la fermeture des connexions, la vérification de l'existence d'une connexion, d'un utilisateur ou d'un groupe.
  • Hub fortement typé
  • Nom du hub unifié et paramètre de chaîne de connexion dans un emplacement unique.

Le code suivant montre comment écrire des liaisons Azure SignalR Service dans un modèle basé sur des classe :

tout d'abord, définissez votre hub dérivé d'une classe ServerlessHub:

[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub
{
    private const string HubName = nameof(Functions); // Used by SignalR trigger only

    public Functions(IServiceProvider serviceProvider) : base(serviceProvider)
    {
    }

    [Function("negotiate")]
    public async Task<HttpResponseData> Negotiate([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
    {
        var negotiateResponse = await NegotiateAsync(new() { UserId = req.Headers.GetValues("userId").FirstOrDefault() });
        var response = req.CreateResponse();
        response.WriteBytes(negotiateResponse.ToArray());
        return response;
    }

    [Function("Broadcast")]
    public Task Broadcast(
    [SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
    {
        return Clients.All.SendAsync("newMessage", new NewMessage(invocationContext, message));
    }

    [Function("JoinGroup")]
    public Task JoinGroup([SignalRTrigger(HubName, "messages", "JoinGroup", "connectionId", "groupName")] SignalRInvocationContext invocationContext, string connectionId, string groupName)
    {
        return Groups.AddToGroupAsync(connectionId, groupName);
    }
}

dans le fichier Program.cs , enregistrez votre hub serverless :

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(b => b.Services
        .AddServerlessHub<Functions>())
    .Build();

Expérience de négociation dans le cadre d'un modèle basé sur les classes

Au lieu d’utiliser la liaison d’entrée SignalR [SignalRConnectionInfoInput], la négociation dans un modèle basé sur la classe peut être plus flexible. La classe de base ServerlessHub possède une méthode NegotiateAsync, qui permet aux utilisateurs de personnaliser les options de négociation telles que userId, claims, etc.

Task<BinaryData> NegotiateAsync(NegotiationOptions? options = null)

Envoi de messages et gestion de l'expérience dans un modèle basé sur les classes

Vous pouvez envoyer des messages, gérer des groupes ou des clients en accédant aux membres fournis par la classe de base ServerlessHub.

  • ServerlessHub.Clients pour l'envoi de messages aux clients.
  • ServerlessHub.Groups pour gérer les connexions avec des groupes, telles que l'ajout de connexions à des groupes, la suppression des connexions des groupes.
  • ServerlessHub.UserGroups pour gérer les utilisateurs avec des groupes, tels que l'ajout d'utilisateurs à des groupes, la suppression d'utilisateurs de groupes.
  • ServerlessHub.ClientManager pour vérifier l'existence des connexions, fermer les connexions, etc.

Hub fortement typé

Le Hub fortement typévous permet d'utiliser des méthodes fortement typées lorsque vous envoyez des messages à des clients. Pour utiliser un hub fortement typé dans un modèle basé sur des classes, extrayez les méthodes du client dans une interface T. Ensuite, faites en sorte que votre classe de hub soit dérivée de ServerlessHub<T>.

Le code suivant est un échantillon d'interface pour les méthodes clientes.

public interface IChatClient
{
    Task newMessage(NewMessage message);
}

Vous pouvez ensuite utiliser les méthodes fortement typées comme suit :

[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub<IChatClient>
{
    private const string HubName = nameof(Functions);  // Used by SignalR trigger only

    public Functions(IServiceProvider serviceProvider) : base(serviceProvider)
    {
    }

    [Function("Broadcast")]
    public Task Broadcast(
    [SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
    {
        return Clients.All.newMessage(new NewMessage(invocationContext, message));
    }
}

Remarque

Vous pouvez obtenir un échantillon de projet complet à partir de GitHub.

Nom du hub unifié et paramètre de chaîne de connexion dans un emplacement unique

  • Le nom de la classe du hub serverless est automatiquement utilisé comme HubName.
  • Vous avez peut-être remarqué que l'attribut SignalRConnection utilisé sur les classes de hub serverless se présente comme suit :
    [SignalRConnection("AzureSignalRConnectionString")]
    public class Functions : ServerlessHub<IChatClient>
    
    Il vous permet de personnaliser l'emplacement de la chaîne de connexion pour le hub serverless. S'il est absent, la valeur par défaut AzureSignalRConnectionString est utilisée.

Important

Les déclencheurs Azure SignalR Service et les hubs serverless sont indépendants. Par conséquent, le nom de classe du hub serverless et de l'attribut SignalRConnection ne modifie pas les paramètres des déclencheurs Azure SignalR Service, même si vous utilisez des déclencheurs SignalR à l'intérieur du hub serverless.

Développement client

Les applications clientes SignalR peuvent utiliser le SDK client SignalR dans un des langages pour se connecter facilement à Azure SignalR Service et recevoir des messages de ce dernier.

Configuration d’une connexion cliente

Pour se connecter à SignalR Service, un client doit mener à bien une négociation de connexion qui se compose des étapes suivantes :

  1. Effectuer une demande au point de terminaison negotiate HTTP mentionné ci-dessus afin d'obtenir des informations de connexion valides.
  2. Se connecter à Azure SignalR Service en utilisant l'URL du point de terminaison de service et le jeton d'accès obtenu à partir du point de terminaison negotiate.

Les SDK clients SignalR contiennent déjà la logique nécessaire pour effectuer la procédure de négociation. Transmettre l'URL du point de terminaison de la négociation, moins le segment negotiate, au HubConnectionBuilder du Kit de développement logiciel (SDK). Voici un exemple en JavaScript :

const connection = new signalR.HubConnectionBuilder()
  .withUrl("https://my-signalr-function-app.azurewebsites.net/api")
  .build();

Par convention, le SDK ajoute automatiquement /negotiate à l’URL et l’utilise pour commencer la négociation.

Remarque

Si vous utilisez le SDK JavaScript/TypeScript dans un navigateur, vous devez activer le partage de ressources entre origines multiples (CORS, Cross-Origin Resource Sharing) sur votre application de fonction.

Pour plus d’informations sur l’utilisation du SDK client SignalR, consultez la documentation de votre langage :

Envoi de messages à partir d’un client au service

Si votre ressource SignalR bénéficie d’une configuration en amont, vous pouvez envoyer des messages entre un client et votre instance Azure Functions à l’aide de n’importe quel client SignalR. Voici un exemple en JavaScript :

connection.send("method1", "arg1", "arg2");

Configuration Azure Functions

Les applications de fonction Azure qui s’intègrent à Azure SignalR Service peuvent être déployées comme toute application de fonction Azure classique, à l’aide de techniques telles que le déploiement en continu, le déploiement Zip et l’exécution à partir du package.

Toutefois, il existe quelques considérations spéciales pour les applications qui utilisent les liaisons SignalR Service. Si le client s’exécute dans un navigateur, CORS doit être activé. Enfin, si l'application nécessite une authentification, vous pouvez intégrer le point de terminaison de la négociation avec l'authentificationApp Service.

Activation de CORS

Le client JavaScript/TypeScript adresse une requête HTTP à la fonction de négociation pour lancer la négociation de la connexion. Lorsque l'application cliente est hébergée sur un domaine différent de celui de l'application Azure Function, le partage des ressources inter-origin (CORS) doit être activé sur l'application de fonction, sinon le navigateur bloquera les requêtes.

Localhost

Quand vous exécutez l’application de fonction sur votre ordinateur local, vous pouvez ajouter une section Host à local.settings.json pour activer CORS. Dans la section Host, ajoutez deux propriétés :

  • CORS : entrez l’URL de base qui est l’origine de l’application cliente
  • CORSCredentials : définissez cette propriété sur true pour autoriser les demandes « withCredentials »

Exemple :

{
  "IsEncrypted": false,
  "Values": {
    // values
  },
  "Host": {
    "CORS": "http://localhost:8080",
    "CORSCredentials": true
  }
}

Cloud - CORS Azure Functions

Pour activer CORS sur une application de fonction Azure, accédez à l’écran de configuration de CORS sous l’onglet Fonctionnalités de la plateforme de votre application de fonction dans le portail Azure.

Remarque

La configuration CORS n’est pas encore disponible dans le plan de consommation Linux Azure Functions. Utilisez Gestion des API Azure pour activer CORS.

CORS doit être activé avec Access-Control-Allow-Credentials pour que le client Azure SignalR Service puisse appeler la fonction de négociation. Pour l'activer, cochez la case correspondante.

Dans la section Origines autorisées, ajoutez une entrée contenant l’URL de base d’origine de votre application web.

Configuration de CORS

Cloud - Gestion des API Azure

La gestion des API Azure fournit une passerelle API qui ajoute des capacités aux services back-end existants. Vous pouvez l’utiliser pour ajouter CORS à votre application de fonction. Elle offre un niveau de consommation avec un tarif de paiement par action et un octroi gratuit mensuel.

Pour plus d’informations sur la façon d’importer une application Azure Function, reportez-vous à la documentation Gestion des API. Une fois l’importation effectuée, vous pouvez ajouter une stratégie de trafic entrant pour activer CORS avec une prise en charge d’Access-Control-Allow-Credentials.

<cors allow-credentials="true">
  <allowed-origins>
    <origin>https://azure-samples.github.io</origin>
  </allowed-origins>
  <allowed-methods>
    <method>GET</method>
    <method>POST</method>
  </allowed-methods>
  <allowed-headers>
    <header>*</header>
  </allowed-headers>
  <expose-headers>
    <header>*</header>
  </expose-headers>
</cors>

Configurez vos clients SignalR pour qu’ils utilisent l’URL de gestion des API.

Utilisation de l’authentification App Service

Azure Functions dispose d'une authentification intégrée, prenant en charge les fournisseurs les plus courants tels que Facebook, Twitter, Microsoft Account, Google et Microsoft Entra ID. Cette caractéristique peut être intégrée à la liaison SignalRConnectionInfo pour créer des connexions à Azure SignalR Service authentifié par un identifiant utilisateur. Votre application peut envoyer des messages à l'aide de la liaison de sortie SignalR qui sont destinés à cet identifiant utilisateur.

Dans le portail Azure, sous l’onglet Fonctionnalités de la plateforme de votre application de fonction, ouvrez la fenêtre de paramètres Authentification/autorisation. Consultez la documentation relative à l’authentification App Service pour configurer l’authentification à l’aide du fournisseur d’identité de votre choix.

Une fois configurées, les requêtes HTTP authentifiées comprennent des en-têtes x-ms-client-principal-name et x-ms-client-principal-id contenant respectivement le nom d'utilisateur et l'identifiant utilisateur authentifié.

Vous pouvez utiliser ces en-têtes dans votre configuration de liaison SignalRConnectionInfo pour créer des connexions authentifiées. Voici un exemple de fonction de négociation C# qui utilise l'en-tête x-ms-client-principal-id.

[FunctionName("negotiate")]
public static SignalRConnectionInfo Negotiate(
    [HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
    [SignalRConnectionInfo
        (HubName = "chat", UserId = "{headers.x-ms-client-principal-id}")]
        SignalRConnectionInfo connectionInfo)
{
    // connectionInfo contains an access key token with a name identifier claim set to the authenticated user
    return connectionInfo;
}

Vous pouvez ensuite envoyer des messages à cet utilisateur en définissant la propriété UserId d’un message SignalR.

[FunctionName("SendMessage")]
public static Task SendMessage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")]object message,
    [SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
{
    return signalRMessages.AddAsync(
        new SignalRMessage
        {
            // the message will only be sent to these user IDs
            UserId = "userId1",
            Target = "newMessage",
            Arguments = new [] { message }
        });
}

Pour plus d’informations sur les autres langages, consultez les informations de référence sur les liaisons Azure SignalR Service pour Azure Functions.

Étapes suivantes

Le présent article vous apprend à développer et à configurer des applications Azure SignalR Service serverless à l'aide d'Azure Functions. Essayez de créer une application vous-même à l’aide d’un des guides de démarrage rapide ou tutoriels proposés dans la page de vue d’ensemble de SignalR Service.