Sviluppo e configurazione di Funzioni di Azure e con il Servizio Azure SignalR

Le applicazioni di Funzioni di Azure possono usare le associazioni del Servizio Azure SignalR per aggiungere funzionalità in tempo reale. Le applicazioni client usano gli SDK client disponibili in diverse lingue per connettersi al Servizio Azure SignalR e ricevere messaggi in tempo reale.

Questo articolo descrive i concetti per lo sviluppo e la configurazione di un'app per le funzioni di Azure integrata con il servizio SignalR.

Configurazione del servizio SignalR

Il Servizio Azure SignalR può essere configurato in modalità diverse. Se usato con Funzioni di Azure, il servizio deve essere configurato in modalità serverless.

Nel portale di Azure individuare la pagina Impostazioni della risorsa del servizio SignalR. Impostare Modalità servizio su Serverless.

Modalità del servizio SignalR

Sviluppo di Funzioni di Azure

Un'applicazione serverless in tempo reale compilata con Funzioni di Azure e con il servizio Azure SignalR richiede almeno due istanze di Funzioni di Azure:

  • Una funzione negotiate che il client chiama per ottenere un token di accesso valido del servizio SignalR e un URL dell'endpoint.
  • Una o più funzioni che gestiscono i messaggi inviati dal servizio SignalR ai client.

Funzione di negoziazione

Un'applicazione client richiede un token di accesso valido per connettersi al Servizio Azure SignalR. Un token di accesso può essere anonimo o autenticato in un ID utente. Le applicazioni del servizio SignalR serverless richiedono un endpoint HTTP denominato negotiate per ottenere un token e altre informazioni di connessione, ad esempio l'URL dell'endpoint del servizio SignalR.

Usare una funzione di Azure attivata da HTTP e l'associazione di input SignalRConnectionInfo per generare l'oggetto con le informazioni di connessione. La funzione deve avere un percorso HTTP che termina in /negotiate.

Con il modello basato su classi in C# non è necessaria l'associazione di input SignalRConnectionInfo ed è possibile aggiungere attestazioni personalizzate molto più facilmente. Per altre informazioni, vedere Esperienza di negoziazione nel modello basato su classi.

Per altre informazioni sulla funzione negotiate, vedere Sviluppo di Funzioni di Azure.

Per informazioni su come creare un token autenticato, vedere Uso dell'autenticazione di Servizio app.

Gestire i messaggi inviati dal servizio SignalR

Usare l'associazione SignalRTrigger per gestire i messaggi inviati dal servizio SignalR. È possibile ricevere una notifica quando i client inviano messaggi o i client vengono connessi o disconnessi.

Per altre informazioni, vedere informazioni di riferimento sull'associazione di trigger del servizio SignalR.

È anche necessario configurare l'endpoint della funzione come endpoint upstream in modo che il servizio attivi la funzione quando viene visualizzato un messaggio da un client. Per altre informazioni su come configurare gli endpoint upstream, vedere Endpoint upstream.

Nota

Il servizio SignalR non supporta il messaggio StreamInvocation da un client in modalità serverless.

Invio di messaggi e gestione dell'appartenenza ai gruppi

Usare l'associazione di output SignalR per inviare messaggi ai client connessi al Servizio Azure SignalR. È possibile trasmettere messaggi a tutti i client oppure inviarli a un subset di client. Ad esempio, inviare messaggi solo ai client autenticati con un ID utente specifico o solo a un gruppo specifico.

Gli utenti possono essere aggiunti a uno o più gruppi. È anche possibile usare l'associazione di output SignalR per aggiungere o rimuovere utenti da/verso i gruppi.

Per altre informazioni, vedere il SignalR riferimento all'associazione di output.

Hub SignalR

SignalR ha un concetto di hub. Ogni connessione client e ogni messaggio inviato da Funzioni di Azure ha come ambito un hub specifico. È possibile usare hub come modo per separare le connessioni e i messaggi in spazi dei nomi logici.

Modello basato su classi

Il modello basato su classi è dedicato per C#.

Il modello basato su classi offre un'esperienza di programmazione migliore, che può sostituire le associazioni di input e output SignalR, con le funzionalità seguenti:

  • Negoziazione più flessibile, invio di messaggi e gestione dei gruppi.
  • Sono supportate altre funzionalità di gestione, tra cui la chiusura delle connessioni, la verifica dell'esistenza di una connessione, di un utente o di un gruppo.
  • Hub tipizzati in modo sicuro
  • Nome dell'hub unificato e impostazione della stringa di connessione in un'unica posizione.

Il codice seguente illustra come scrivere associazioni SignalR nel modello basato su classi:

In primo luogo, definire l'hub derivato da una 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);
    }
}

Nel file Program.cs registrare l'hub serverless:

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

Esperienza di negoziazione nel modello basato su classi

Anziché usare l'associazione di input [SignalRConnectionInfoInput] SignalR, la negoziazione nel modello basato su classi può essere più flessibile. La classe base ServerlessHub ha un metodo NegotiateAsync che consente agli utenti di personalizzare le opzioni di negoziazione, ad esempio userId, claims e così via.

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

Invio di messaggi e gestione dell'esperienza nel modello basato su classi

È possibile inviare messaggi, gestire gruppi o gestire i client accedendo ai membri forniti dalla classe base ServerlessHub.

  • ServerlessHub.Clients per l'invio di messaggi ai client.
  • ServerlessHub.Groups per la gestione delle connessioni con gruppi, ad esempio l'aggiunta di connessioni ai gruppi, la rimozione di connessioni dai gruppi.
  • ServerlessHub.UserGroups per la gestione degli utenti con gruppi, ad esempio l'aggiunta di utenti a gruppi, la rimozione di utenti dai gruppi.
  • ServerlessHub.ClientManager per verificare l'esistenza delle connessioni, la chiusura di connessioni e così via.

Hub tipizzati in modo sicuro

L'hub fortemente tipizzato consente di usare metodi fortemente tipizzati quando si inviano messaggi ai client. Per usare un hub fortemente tipizzato nel modello basato su classi, estrarre i metodi client in un'interfaccia T e rendere la classe hub derivata da ServerlessHub<T>.

Il codice seguente è un esempio di interfaccia per i metodi client.

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

È quindi possibile usare i metodi fortemente tipizzato come segue:

[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));
    }
}

Nota

È possibile ottenere un esempio di progetto completo da GitHub.

Nome dell'hub unificato e impostazione della stringa di connessione in un'unica posizione

  • Il nome della classe dell'hub serverless viene usato automaticamente come HubName.
  • È possibile che l'attributo SignalRConnection usato nelle classi dell'hub serverless sia stato notato come segue:
    [SignalRConnection("AzureSignalRConnectionString")]
    public class Functions : ServerlessHub<IChatClient>
    
    Consente di personalizzare la posizione in cui si trova la stringa di connessione per l'hub serverless. Se è assente, viene usato il valore predefinito AzureSignalRConnectionString.

Importante

I trigger SignalR e gli hub serverless sono indipendenti. Pertanto, il nome della classe dell'hub serverless e dell'attributo SignalRConnection non modifica le impostazioni dei trigger SignalR, anche se si usano trigger SignalR all'interno dell'hub serverless.

Sviluppo client

Le applicazioni client SignalR possono usare l'SDK client SignalR in uno dei diversi linguaggi per connettersi e ricevere facilmente messaggi dal Servizio Azure SignalR.

Configurazione di una connessione client

Per connettersi al servizio SignalR, un client deve completare una negoziazione di connessione riuscita costituita da questi passaggi:

  1. Effettuare una richiesta all'endpoint HTTP negotiate descritto in precedenza per ottenere informazioni di connessione valide
  2. Connettersi al servizio SignalR usando l'URL dell'endpoint di servizio e il token di accesso ottenuti dall'endpoint negotiate

Gli SDK del client SignalR contengono già la logica necessaria per eseguire l'handshake di negoziazione. Passare l'URL dell'endpoint di negoziazione, meno il segmento negotiate a HubConnectionBuilder dell'SDK. Ecco un esempio in JavaScript:

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

Per convenzione, l'SDK aggiunge automaticamente /negotiate all'URL e lo usa per avviare la negoziazione.

Nota

Se si usa JavaScript/TypeScript SDK in un browser, è necessario abilitare la condivisione di risorse tra le origini (CORS) nell'app per le funzioni.

Per altre informazioni su come usare l'SDK client SignalR, vedere la documentazione relativa al linguaggio:

Invio di messaggi da un client al servizio

Se è stato configurato upstream per la risorsa SignalR, è possibile inviare messaggi da un client a Funzioni di Azure usando qualsiasi client SignalR. Ecco un esempio in JavaScript:

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

Configurazione di Funzioni di Azure

Le app per le funzioni di Azure che si integrano con il Servizio Azure SignalR possono essere distribuite come qualsiasi tipica app per le funzioni di Azure usando tecniche come la distribuzione continua, la distribuzione zip e l'esecuzione dal pacchetto.

Esistono tuttavia alcune considerazioni speciali per le app che usano le associazioni del servizio SignalR. Se il client viene eseguito in un browser, è necessario abilitare CORS. Se l'app richiede l'autenticazione, è possibile integrare l'endpoint di negoziazione con l'autenticazione del servizio app.

Abilitazione della condivisione CORS

Il client JavaScript/TypeScript effettua una richiesta HTTP alla funzione di negoziazione per avviare la negoziazione della connessione. Quando l'applicazione client è ospitata in un dominio diverso rispetto all'app per le funzioni di Azure, è necessario abilitare la condivisione di risorse tra le origini (CORS) nell'app per le funzioni oppure il browser bloccherà le richieste.

Localhost

Quando si esegue l'app per le funzioni nel computer locale, è possibile aggiungere una sezione Host a local.settings.json per abilitare CORS. Nella sezione Host aggiungere due proprietà:

  • CORS: immettere l'URL di base che rappresenta l'origine dell'applicazione client
  • CORSCredentials: impostarlo su true per consentire le richieste "withCredentials"

Esempio:

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

Cloud - CORS di Funzioni di Azure

Per abilitare CORS in un'app per le funzioni di Azure, passare alla schermata di configurazione CORS nella scheda Funzionalità della piattaforma dell'app per le funzioni nel portale di Azure.

Nota

La configurazione CORS non è ancora disponibile nel piano a consumo per Linux di Funzioni di Azure. Usare Gestione API di Azure per abilitare CORS.

CORS con Access-Control-Allow-Credentials deve essere abilitato per consentire al client SignalR di chiamare la funzione di negoziazione. Per abilitarla, selezionare la casella di controllo.

Nella sezione Origini consentite aggiungere una voce con l'URL di base di origine dell'applicazione Web.

Configurazione di CORS

Cloud - Gestione API di Azure

Gestione API di Azure offre un gateway API che aggiunge funzionalità ai servizi back-end esistenti. È possibile usarlo per aggiungere CORS all'app per le funzioni. Offre un piano a consumo con prezzi con pagamento in base all'azione e una concessione gratuita mensile.

Per informazioni su come importare un'app per le funzioni di Azure, vedere la documentazione di Gestione API. Dopo l'importazione, è possibile aggiungere un criterio in ingresso per abilitare CORS con il supporto di 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>

Configurare i client SignalR per l'uso dell'URL di Gestione API.

Utilizzo di Autenticazione servizio app

Funzioni di Azure include l'autenticazione predefinita, supportando provider diffusi come Facebook, X, Account Microsoft, Google e Microsoft Entra ID. Questa funzionalità può essere integrata con l'associazione SignalRConnectionInfo per creare connessioni al Servizio Azure SignalR autenticato in un ID utente. L'applicazione può inviare messaggi usando l'associazione di output SignalR destinata a tale ID utente.

Nella scheda Funzionalità della piattaforma dell'app per le funzioni del portale di Azure aprire la finestra delle impostazioni di Autenticazione/autorizzazione. Seguire la documentazione relativa all'autenticazione del servizio app per configurare l'autenticazione usando un provider di identità di propria scelta.

Dopo la configurazione, le richieste HTTP autenticate includono le intestazioni x-ms-client-principal-name e x-ms-client-principal-id contenenti rispettivamente il nome utente e l'ID utente dell'identità autenticata.

È possibile usare queste intestazioni nella configurazione dell'associazione SignalRConnectionInfo per creare connessioni autenticate. Ecco un esempio di funzione di negoziazione C# che usa l'intestazione 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;
}

È quindi possibile inviare messaggi all'utente impostando la proprietà UserId di un messaggio 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 }
        });
}

Per informazioni su altri linguaggi, vedere Associazioni del Servizio Azure SignalR come riferimento per le Funzioni di Azure.

Passaggi successivi

Questo articolo illustra come sviluppare e configurare applicazioni del servizio SignalR serverless usando Funzioni di Azure. Provare a creare manualmente un'applicazione usando una delle guide introduttive o le esercitazioni nella pagina di panoramica del servizio SignalR.