Azure Functions-Entwicklung und -Konfiguration mit Azure SignalR Service

Azure Functions-Anwendungen können die Azure SignalR Service-Bindings verwenden, um Echtzeitfunktionen hinzuzufügen. Clientanwendungen verwenden in mehreren Sprachen verfügbare Client-SDKs, um eine Verbindung mit Azure SignalR Service herzustellen und Nachrichten in Echtzeit zu empfangen.

In diesem Artikel werden die Konzepte für die Entwicklung und Konfiguration einer Azure Functions-App mit SignalR Service-Integration beschrieben.

SignalR Service-Konfiguration

Azure SignalR Service kann in verschiedenen Modi konfiguriert werden. Bei Verwendung mit Azure Functions muss der Dienst im serverlosen Modus konfiguriert werden.

Navigieren Sie im Azure-Portal zu Seite Einstellungen Ihrer SignalR Service-Ressource. Legen Sie den Dienstmodus auf Serverlos fest.

SignalR Service-Modus

Azure Functions-Entwicklung

Eine serverlose Echtzeitanwendung, die mit Azure Functions und Azure SignalR Service erstellt wurde, erfordert mindestens zwei Azure Functions:

  • Eine negotiateFunktion, die der Client aufruft, um ein gültiges SignalR Service-Zugangs-Token und eine Endpunkt-URL zu erhalten.
  • Eine oder mehrere Funktionen, die Nachrichten verarbeiten, die vom SignalR Service an Clients gesendet werden.

Aushandlungsfunktion

Eine Clientanwendung benötigt ein gültiges Zugriffstoken, um eine Verbindung mit Azure SignalR Service herstellen zu können. Ein Zugriffstoken kann anonym oder für eine Benutzer-ID authentifiziert sein. Serverlose SignalR Service-Anwendungen erfordern einen HTTP-Endpunkt mit dem Namen negotiate, um ein Token und andere Verbindungsinformationen wie die SignalR Service-Endpunkt-URL abzurufen.

Verwenden Sie eine Azure-Funktion mit HTTP-Trigger sowie dieSignalRConnectionInfo Eingabebindung, um das Objekt für Verbindungsinformationen zu generieren. Die Funktion muss über eine HTTP-Route verfügen, die mit /negotiate endet.

Mit einem klassenbasierten Modell in C# benötigen Sie keine SignalRConnectionInfoEingangsbindung können benutzerdefinierte Ansprüche viel einfacher hinzugefügt werden. Weitere Informationen finden Sie unter Aushandlung im klassenbasierten Modell.

Weitere Informationen zur negotiate Funktion finden Sie unter Azure Functions Entwicklung.

Informationen zum Erstellen eines authentifizierten Tokens finden Sie unter Verwenden der App Service-Authentifizierung.

Verarbeiten von Nachrichten, die von SignalR Service gesendet wurden

Verwenden Sie die SignalRTrigger-Bindung, um von SignalR Service gesendete Nachrichten zu verarbeiten. Sie können sich benachrichtigen lassen, wenn Clients Nachrichten senden oder wenn eine Clientverbindung hergestellt oder getrennt wird.

Weitere Informationen finden Sie in der Referenz zur SignalR Service-Trigger-Bindung.

Außerdem müssen Sie Ihren Funktionsendpunkt als Upstream-Endpunkt konfigurieren, damit der Dienst die Funktion auslöst, wenn eine Nachricht von einem Client vorliegt. Weitere Informationen zum Konfigurieren von Upstream-Endpunkten finden Sie in unter Upstream-Endpunkte.

Hinweis

SignalR Service unterstützt nicht die StreamInvocationNachricht von einem Client im Serverlos Mode.

Senden von Nachrichten und Verwalten der Gruppenmitgliedschaft

Verwenden Sie die Ausgabebindung SignalR, um Nachrichten an Clients zu senden, die mit Azure SignalR Service verbunden sind. Sie können Nachrichten an alle Clients oder an eine Teilmenge von Clients senden. Senden Sie zum Beispiel nur Nachrichten an Clients, die mit einer bestimmten Benutzer-ID authentifiziert sind, oder nur an eine bestimmte Gruppe.

Benutzer können einer einzelnen Gruppe oder mehreren Gruppen hinzugefügt werden. Mit der Ausgabebindung SignalR können Benutzer auch zu Gruppen hinzugefügt oder aus Gruppen entfernt werden.

Weitere Informationen finden Sie in der SignalRReferenz zur Ausgabebindung.

SignalR-Hubs

SignalR hat ein Konzept von hubs. Jede Clientverbindung und jede aus Azure Functions gesendete Nachricht ist auf einen bestimmten Hub ausgerichtet. Mithilfe von Hubs können Sie Ihre Verbindungen und Nachrichten in logische Namespaces aufteilen.

Klassenbasiertes Modell

Das klassenbasierte Modell ist dediziert für C# bestimmt.

Das klassenbasierte Modell bietet eine bessere Programmiererfahrung, die SignalR-Eingabe- und Ausgabebindungen durch die folgenden Features ersetzen kann:

  • Flexiblere Aushandlung, Senden von Nachrichten und Verwalten von Gruppen.
  • Weitere Verwaltungsfunktionen werden unterstützt, einschließlich schließender Verbindungen und dem Überprüfen, ob eine Verbindung, ein Benutzer oder eine Gruppe vorhanden ist.
  • Stark typisierter Hub
  • Einheitliche Einstellung von Hub-Namen und Verbindungszeichenfolge an einer Stelle.

Der folgende Code veranschaulicht, wie SignalR-Bindungen im klassenbasierten Modell geschrieben werden:

Definieren Sie zunächst Ihren Hub, der von einer ServerlessHub-Klasse abgeleitet wurde:

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

Registrieren Sie in der Datei Program.cs Ihren serverlosen Hub:

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

Aushandlung im klassenbasierten Modell

Anstatt die SignalR-Eingangsbindung [SignalRConnectionInfoInput] zu verwenden, kann die Verhandlung im klassenbasierten Modell flexibler erfolgen. Die Basisklasse ServerlessHub verfügt über eine Methode NegotiateAsync, mit der Benutzer Aushandlungsoptionen wie userId, claims usw. anpassen können.

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

Senden von Nachrichten und Verwalten von Erfahrungen im klassenbasierten Modell

Sie können Nachrichten senden, Gruppen verwalten oder Clients verwalten, indem Sie auf die Mitglieder zugreifen, die von der Basisklasse ServerlessHub bereitgestellt werden.

  • ServerlessHub.Clients zum Senden von Nachrichten an Clients.
  • ServerlessHub.Groups zum Verwalten von Verbindungen mit Gruppen, z. B. zum Hinzufügen von Verbindungen zu Gruppen oder zum Entfernen daraus.
  • ServerlessHub.UserGroups zum Verwalten von Benutzern mit Gruppen, z. B. zum Hinzufügen von Benutzern zu Gruppen oder zum Entfernen daraus.
  • ServerlessHub.ClientManager zum Überprüfen des Vorhandenseins von Verbindungen, zum Schließen von Verbindungen, usw.

Stark typisierter Hub

Stark typisierter Hub ermöglicht es Ihnen, stark typisierte Methoden zu verwenden, wenn Sie Nachrichten an Clients senden. Um stark typisierte Hubs im klassenbasierten Modell zu verwenden, extrahieren Sie Clientmethoden in eine Schnittstelle T, und leiten Sie Ihre Hubklasse von ServerlessHub<T> ab.

Der folgende Code ist ein Schnittstellenbeispiel für Clientmethoden.

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

Anschließend können Sie die stark typisierten Methoden wie folgt verwenden:

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

Hinweis

Sie können ein vollständiges Projektbeispiel aus GitHub abrufen.

Einheitliche Einstellung von Hub-Namen und Verbindungszeichenfolge an einer Stelle

  • Der Klassenname des serverlosen Hubs wird automatisch als HubName verwendet.
  • Möglicherweise haben Sie das SignalRConnection-Attribut bemerkt, das in serverlosen Hubklassen wie folgt verwendet wird:
    [SignalRConnection("AzureSignalRConnectionString")]
    public class Functions : ServerlessHub<IChatClient>
    
    Damit können Sie anpassen, wo sich die Verbindungszeichenfolge für serverlose Hubs befindet. Wenn nichts angegeben wird, wird der Standardwert AzureSignalRConnectionString verwendet.

Wichtig

SignalR-Trigger und serverlose Hubs sind unabhängig. Daher ändert der Klassenname des serverlosen Hubs und des SignalRConnection-Attributs nicht die Einstellungen von SignalR-Triggern, auch wenn Sie SignalR-Trigger innerhalb des serverlosen Hubs verwenden.

Cliententwicklung

SignalR-Client-Anwendungen können das SignalR-Client-SDK in einer von mehreren Sprachen verwenden, um sich einfach mit dem Azure SignalR Service zu verbinden und Nachrichten von diesem zu empfangen.

Konfigurieren einer Clientverbindung

Für die Verbindungsherstellung mit SignalR Service ist eine erfolgreiche Verbindungsaushandlung durch den Client erforderlich. Diese umfasst folgende Schritte:

  1. Senden einer Anforderung an den weiter oben besprochenen HTTP-Endpunkt negotiate, um gültige Verbindungsinformationen zu erhalten
  2. Herstellen der Verbindung mit SignalR Service unter Verwendung der Dienstendpunkt-URL und des Zugriffstokens, die vom Endpunkt negotiate bezogen wurden

Die für den Aushandlungs-Handshake erforderliche Logik ist bereits in den SignalR-Client-SDKs enthalten. Übergeben Sie die URL des Aushandlungsendpunkts (ohne das Segment negotiate) an HubConnectionBuilder des SDKs. Hier ist ein Beispiel in JavaScript:

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

Konventionsgemäß fügt das SDK automatisch /negotiate an die URL an und verwendet sie, um mit der Aushandlung zu beginnen.

Hinweis

Falls Sie das JavaScript/TypeScript SDK in einem Browser verwenden, müssen Sie in Ihrer Funktionen-App die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS) aktivieren.

Weitere Informationen zur Verwendung des SignalR-Client-SDK finden Sie in der Dokumentation zu Ihrer Sprache:

Senden von Nachrichten von einem Client an den Dienst

Wenn Sie Ihre SignalR-Ressource im Upstream konfiguriert haben, können Sie mit einem beliebigen SignalR-Client Nachrichten von einem Client an Ihre Azure Functions senden. Hier ist ein Beispiel in JavaScript:

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

Azure Functions-Konfiguration

Azure-Funktions-Apps mit Azure SignalR Service-Integration können wie jede typische Azure-Funktions-App mit Techniken wie Continuous Deployment, ZIP-Bereitstellung und Ausführen aus einem Paket bereitgestellt werden.

Bei Apps mit SignalR Service-Bindungen müssen allerdings ein paar Besonderheiten berücksichtigt werden. Wenn der Client in einem Browser ausgeführt wird, muss CORS aktiviert werden. Sollte die App eine Authentifizierung erfordern, können Sie den Aushandlungsendpunkt in die App Service-Authentifizierung integrieren.

Aktivieren von CORS

Der JavaScript/TypeScript-Client stellt eine HTTP-Anforderung an die Aushandlungsfunktion, um die Verbindungsaushandlung einzuleiten. Wird die Clientanwendung in einer anderen Domäne gehostet als die Azure-Funktions-App, muss für die Funktions-App die Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS) aktiviert werden, da der Browser die Anforderungen andernfalls blockiert.

Localhost

Wenn Sie die Funktions-App auf Ihrem lokalen Computer ausführen, können Sie local.settings.json einen Abschnitt vom Typ Host hinzufügen, um CORS zu aktivieren. Fügen Sie im Abschnitt Host zwei Eigenschaften hinzu:

  • CORS: Geben Sie die Basis-URL (also den Ursprung der Clientanwendung) ein.
  • CORSCredentials: Legen Sie diese Eigenschaft auf true fest, um Anforderungen vom Typ „withCredentials“ zu ermöglichen.

Beispiel:

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

Cloud – Azure Functions-CORS

Wenn Sie CORS für eine Azure-Funktions-App aktivieren möchten, navigieren Sie im Azure-Portal unter der Registerkarte Plattformfeatures Ihrer Funktions-App zum CORS-Konfigurationsbildschirm.

Hinweis

Die CORS-Konfiguration ist im Linux-Verbrauchstarif für Azure Functions noch nicht verfügbar. Verwenden Sie Azure API Management, um CORS zu aktivieren.

CORS muss mit „Access-Control-Allow-Credentials“ aktiviert werden, damit der SignalR-Client die Aushandlungsfunktion aufrufen kann. Aktivieren Sie das entsprechende Kontrollkästchen.

Fügen Sie im Abschnitt Zulässige Ursprünge einen Eintrag mit der Basis-URL des Ursprungs Ihrer Webanwendung hinzu.

Konfigurieren von CORS

Cloud – Azure API Management

Azure API Management bietet ein API-Gateway, mit dem vorhandenen Back-End-Diensten Funktionen hinzugefügt werden. Sie können damit Ihrer Funktions-App CORS hinzufügen. Es bietet einen Verbrauchstarif mit aktionsbasierten Preisen und einem monatlichen kostenlosen Verbrauch.

Informationen zum Importieren einer Azure-Funktions-App finden Sie in der API Management-Dokumentation. Nach dem Importieren können Sie eine Richtlinie für eingehenden Datenverkehr hinzufügen, um CORS mit der Unterstützung von „Access-Control-Allow-Credentials“ zu aktivieren.

<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>

Konfigurieren Sie die SignalR-Clients für die Verwendung der API Management-URL.

Verwenden der App Service-Authentifizierung

Azure Functions verfügt über eine integrierte Authentifizierung und unterstützt gängige Anbieter wie Facebook, Twitter, Microsoft-Konto, Google und Microsoft Entra ID. Dieses Feature kann in die Bindung SignalRConnectionInfo integriert werden, um Verbindungen mit Azure SignalR Service zu erstellen, die für eine Benutzer-ID authentifiziert wurden. Ihre Anwendung kann unter Verwendung der Ausgabebindung SignalR Nachrichten senden, die für diese Benutzer-ID bestimmt sind.

Öffnen Sie im Azure-Portal auf der Registerkarte Plattformfeatures Ihrer Funktions-App das Fenster mit denAuthentifizierungs-/Autorisierungseinstellungen. Gehen Sie gemäß der Dokumentation für die App Service-Authentifizierung vor, um die Authentifizierung mit einem Identitätsanbieter Ihrer Wahl zu konfigurieren.

Nach Abschluss der Konfiguration enthalten authentifizierte HTTP-Anforderungen Header vom Typ x-ms-client-principal-name und x-ms-client-principal-id, die wiederum den Benutzernamen bzw. die Benutzer-ID der authentifizierten Identität enthalten.

Diese Header können in der Konfiguration der Bindung SignalRConnectionInfo verwendet werden, um authentifizierte Verbindungen zu erstellen. Hier ist ein Beispiel für eine C#-Aushandlungsfunktion mit dem x-ms-client-principal-id-Header verwendet.

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

Anschließend können Sie Nachrichten an diesen Benutzer senden, indem Sie die Eigenschaft UserId einer SignalR-Nachricht festlegen.

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

Informationen zu anderen Sprachen finden Sie unter Bindungen des SignalR-Diensts für Azure Functions.

Nächste Schritte

In diesem Artikel lernen Sie, wie Sie serverlose SignalR Service-Anwendungen mit Azure Functions entwickeln und konfigurieren. Versuchen Sie als Nächstes, anhand einer der Schnellstartanleitungen oder eines der Tutorials auf der Übersichtsseite für SignalR Service selbst eine Anwendung zu erstellen.