Azure Functions-utveckling och -konfiguration med Azure SignalR Service

Azure Functions-program kan använda Azure SignalR Service-bindningar för att lägga till realtidsfunktioner. Klientprogram använder klient-SDK:er som är tillgängliga på flera språk för att ansluta till Azure SignalR Service och ta emot realtidsmeddelanden.

I den här artikeln beskrivs begreppen för att utveckla och konfigurera en Azure-funktionsapp som är integrerad med SignalR Service.

SignalR Service-konfiguration

Azure SignalR Service kan konfigureras i olika lägen. När den används med Azure Functions måste tjänsten konfigureras i serverlöst läge.

I Azure-portalen letar du upp sidan Inställningar för din SignalR Service-resurs. Ange Tjänstläge till Serverlös.

SignalR-tjänstläge

Azure Functions-utveckling

Ett serverlöst realtidsprogram som skapats med Azure Functions och Azure SignalR Service kräver minst två Azure Functions:

  • En negotiate funktion som klienten anropar för att hämta en giltig SignalR Service-åtkomsttoken och slutpunkts-URL.
  • En eller flera funktioner som hanterar meddelanden som skickas från SignalR Service till klienter.

Förhandlingsfunktion

Ett klientprogram kräver en giltig åtkomsttoken för att ansluta till Azure SignalR Service. En åtkomsttoken kan vara anonym eller autentiserad till ett användar-ID. Serverlösa SignalR Service-program kräver en HTTP-slutpunkt med namnet negotiate för att hämta en token och annan anslutningsinformation, till exempel SignalR-tjänstens slutpunkts-URL.

Använd en HTTP-utlöst Azure-funktion och indatabindningen SignalRConnectionInfo för att generera anslutningsinformationsobjektet. Funktionen måste ha en HTTP-väg som slutar i /negotiate.

Med klassbaserad modell i C# behöver du inte indatabindningen SignalRConnectionInfo och kan lägga till anpassade anspråk mycket enklare. Mer information finns i Förhandlingsupplevelse i klassbaserad modell.

Mer information om funktionen finns i negotiate Azure Functions-utveckling.

Information om hur du skapar en autentiserad token finns i Använda App Service-autentisering.

Hantera meddelanden som skickas från SignalR Service

Använd bindningen SignalRTrigger för att hantera meddelanden som skickas från SignalR Service. Du kan få ett meddelande när klienter skickar meddelanden eller klienter ansluts eller kopplas från.

Mer information finns i referensen för SignalR Service-utlösarbindning.

Du måste också konfigurera funktionsslutpunkten som en överordnad slutpunkt så att tjänsten utlöser funktionen när det finns ett meddelande från en klient. Mer information om hur du konfigurerar överordnade slutpunkter finns i Överordnade slutpunkter.

Kommentar

SignalR Service stöder inte meddelandet StreamInvocation från en klient i serverlöst läge.

Skicka meddelanden och hantera gruppmedlemskap

Använd utdatabindningen SignalR för att skicka meddelanden till klienter som är anslutna till Azure SignalR Service. Du kan skicka meddelanden till alla klienter eller skicka dem till en delmängd av klienterna. Du kan till exempel bara skicka meddelanden till klienter som autentiserats med ett specifikt användar-ID eller endast till en viss grupp.

Användare kan läggas till i en eller flera grupper. Du kan också använda utdatabindningen SignalR för att lägga till eller ta bort användare till/från grupper.

Mer information finns i referensen för SignalR utdatabindning.

SignalR Hubs

SignalR har ett begrepp om hubbar. Varje klientanslutning och varje meddelande som skickas från Azure Functions är begränsad till en specifik hubb. Du kan använda hubbar som ett sätt att separera dina anslutningar och meddelanden i logiska namnområden.

Klassbaserad modell

Den klassbaserade modellen är dedikerad för C#.

Den klassbaserade modellen ger bättre programmeringsupplevelse, som kan ersätta SignalR-indata- och utdatabindningar med följande funktioner:

  • Mer flexibla förhandlingar, sändning av meddelanden och hantering av grupper.
  • Fler hanteringsfunktioner stöds, inklusive att stänga anslutningar, kontrollera om det finns en anslutning, användare eller grupp.
  • Starkt skriven hubb
  • Enhetligt hubbnamn och niska veze inställning på ett ställe.

Följande kod visar hur du skriver SignalR-bindningar i klassbaserad modell:

För det första definierar du din hubb som härletts från en klass 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);
    }
}

I Program.cs-filen registrerar du din serverlösa hubb:

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

Förhandlingsupplevelse i klassbaserad modell

I stället för att använda SignalR-indatabindning [SignalRConnectionInfoInput]kan förhandling i klassbaserad modell vara mer flexibel. Basklassen ServerlessHub har en metod NegotiateAsync, som gör det möjligt för användare att anpassa förhandlingsalternativ som userId, claimsosv.

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

Skicka meddelanden och hantera upplevelsen i klassbaserad modell

Du kan skicka meddelanden, hantera grupper eller hantera klienter genom att komma åt de medlemmar som tillhandahålls av basklassen ServerlessHub.

  • ServerlessHub.Clients för att skicka meddelanden till klienter.
  • ServerlessHub.Groups för att hantera anslutningar med grupper, till exempel lägga till anslutningar till grupper, ta bort anslutningar från grupper.
  • ServerlessHub.UserGroups för att hantera användare med grupper, till exempel lägga till användare i grupper, ta bort användare från grupper.
  • ServerlessHub.ClientManager för att kontrollera anslutningens existens, stänga anslutningar osv.

Starkt skriven hubb

Med en starkt typad hubb kan du använda starkt skrivna metoder när du skickar meddelanden till klienter. Om du vill använda starkt typad hubb i klassbaserad modell extraherar du klientmetoder till ett gränssnitt Toch gör hubbklassen härledd från ServerlessHub<T>.

Följande kod är ett gränssnittsexempel för klientmetoder.

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

Sedan kan du använda de starkt inskrivna metoderna på följande sätt:

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

Kommentar

Du kan hämta ett fullständigt projektexempel från GitHub.

Enhetligt hubbnamn och niska veze inställning på ett ställe

  • Klassnamnet för den serverlösa hubben används automatiskt som HubName.
  • Du kanske har lagt märke till attributet SignalRConnection som används i serverlösa hubbklasser på följande sätt:
    [SignalRConnection("AzureSignalRConnectionString")]
    public class Functions : ServerlessHub<IChatClient>
    
    Det gör att du kan anpassa var niska veze för serverlös hubb finns. Om den saknas används standardvärdet AzureSignalRConnectionString .

Viktigt!

SignalR-utlösare och serverlösa hubbar är oberoende. Därför ändrar inte klassnamnet för serverlös hubb och SignalRConnection attribut inställningarna för SignalR-utlösare, även om du använder SignalR-utlösare i den serverlösa hubben.

Klientutveckling

SignalR-klientprogram kan använda SignalR-klient-SDK på ett av flera språk för att enkelt ansluta till och ta emot meddelanden från Azure SignalR Service.

Konfigurera en klientanslutning

För att ansluta till SignalR Service måste en klient slutföra en lyckad anslutningsförhandling som består av följande steg:

  1. Skicka en begäran till negotiate HTTP-slutpunkten som beskrivs ovan för att hämta giltig anslutningsinformation
  2. Ansluta till SignalR Service med hjälp av tjänstens slutpunkts-URL och åtkomsttoken som hämtats från negotiate slutpunkten

SignalR-klient-SDK:er innehåller redan den logik som krävs för att utföra handskakningen för förhandlingen. Skicka förhandlingsslutpunktens URL, minus negotiate segmentet, till SDK:ens HubConnectionBuilder. Här är ett exempel i JavaScript:

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

Enligt konventionen läggs SDK automatiskt till /negotiate i URL:en och använder den för att påbörja förhandlingen.

Kommentar

Om du använder JavaScript/TypeScript SDK i en webbläsare måste du aktivera resursdelning mellan ursprung (CORS) i funktionsappen.

Mer information om hur du använder SignalR-klient-SDK finns i dokumentationen för ditt språk:

Skicka meddelanden från en klient till tjänsten

Om du har konfigurerat uppströms för din SignalR-resurs kan du skicka meddelanden från en klient till dina Azure Functions med valfri SignalR-klient. Här är ett exempel i JavaScript:

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

Azure Functions-konfiguration

Azure Function-appar som integreras med Azure SignalR Service kan distribueras som alla vanliga Azure-funktionsappar med hjälp av tekniker som kontinuerlig distribution, zip-distribution och körning från paket.

Det finns dock ett par särskilda överväganden för appar som använder SignalR Service-bindningar. Om klienten körs i en webbläsare måste CORS vara aktiverat. Och om appen kräver autentisering kan du integrera förhandlingsslutpunkten med App Service-autentisering.

Aktivera CORS

JavaScript/TypeScript-klienten skickar HTTP-begäran till förhandlingsfunktionen för att initiera anslutningsförhandlingen. När klientprogrammet finns på en annan domän än Azure-funktionsappen måste cors (cross-origin resource sharing) aktiveras i funktionsappen, annars blockerar webbläsaren begäranden.

Localhost

När du kör funktionsappen på den lokala datorn kan du lägga till ett Host avsnitt i local.settings.json för att aktivera CORS. I avsnittet Host lägger du till två egenskaper:

  • CORS – ange den bas-URL som är ursprunget till klientprogrammet
  • CORSCredentials – ställ in den på true för att tillåta "withCredentials"-begäranden

Exempel:

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

Cloud – Azure Functions CORS

Om du vill aktivera CORS i en Azure-funktionsapp går du till CORS-konfigurationsskärmen under fliken Plattformsfunktioner i funktionsappen i Azure-portalen.

Kommentar

CORS-konfigurationen är ännu inte tillgänglig i förbrukningsplanen för Azure Functions Linux. Använd Azure API Management för att aktivera CORS.

CORS med Access-Control-Allow-Credentials måste vara aktiverat för att SignalR-klienten ska kunna anropa förhandlingsfunktionen. Om du vill aktivera den markerar du kryssrutan.

I avsnittet Tillåtet ursprung lägger du till en post med ursprungsbasens URL för ditt webbprogram.

Konfigurera CORS

Cloud – Azure API Management

Azure API Management tillhandahåller en API-gateway som lägger till funktioner i befintliga serverdelstjänster. Du kan använda den för att lägga till CORS i funktionsappen. Den erbjuder en förbrukningsnivå med priser för betala per åtgärd och ett månatligt kostnadsfritt bidrag.

Mer information om hur du importerar en Azure-funktionsapp finns i API Management-dokumentationen. När du har importerat kan du lägga till en inkommande princip för att aktivera CORS med stöd för Åtkomstkontroll-Tillåt-autentiseringsuppgifter.

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

Konfigurera signalR-klienterna så att de använder API Management-URL:en.

Använda App Service-autentisering

Azure Functions har inbyggd autentisering som stöder populära leverantörer som Facebook, X, Microsoft-konto, Google och Microsoft Entra-ID. Den här funktionen kan integreras med bindningen för att skapa anslutningar till Azure SignalR Service som autentiseras SignalRConnectionInfo till ett användar-ID. Ditt program kan skicka meddelanden med hjälp av utdatabindningen SignalR som är riktad mot användar-ID:t.

Öppna fönstret Inställningar för autentisering/auktorisering på fliken Plattformsfunktioner i funktionsappen i Azure-portalen. Följ dokumentationen för App Service-autentisering för att konfigurera autentisering med valfri identitetsprovider.

När de har konfigurerats inkluderar x-ms-client-principal-name autentiserade HTTP-begäranden och x-ms-client-principal-id rubriker som innehåller den autentiserade identitetens användarnamn respektive användar-ID.

Du kan använda dessa rubriker i bindningskonfigurationen SignalRConnectionInfo för att skapa autentiserade anslutningar. Här är ett exempel på en C#-förhandlingsfunktion som använder x-ms-client-principal-id huvudet.

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

Du kan sedan skicka meddelanden till användaren genom att ange egenskapen för UserId ett SignalR-meddelande.

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

Information om andra språk finns i Azure SignalR Service-bindningar för Azure Functions-referensen.

Nästa steg

I den här artikeln får du lära dig hur du utvecklar och konfigurerar serverlösa SignalR Service-program med hjälp av Azure Functions. Prova att skapa ett program själv med någon av snabbstarterna eller självstudierna på översiktssidan för SignalR Service.