使用 Azure SignalR Service 來開發與設定 Azure Functions

Azure Functions 應用程式可以使用 Azure SignalR Service 系 結來新增即時功能。 用戶端應用程式會使用數種語言提供的用戶端 SDK 來連線到 Azure SignalR 服務並接收即時訊息。

本文說明開發及設定與 SignalR Service 整合的 Azure 函式應用程式的概念。

SignalR 服務組態

Azure SignalR 服務可以在不同的模式設定。 搭配 Azure Functions 使用時,服務必須在無伺服器模式中設定。

在 Azure 入口網站 中,找出 SignalR Service 資源的 [設定] 頁面。 將 [ 服務模式 ] 設定為 [無伺服器]。

SignalR 服務模式

Azure Functions 開發

使用 Azure Functions 和 Azure SignalR Service 建置的無伺服器即時應用程式至少需要兩個 Azure Functions:

  • negotiate用戶端呼叫以取得有效 SignalR 服務存取令牌和端點 URL 的函式。
  • 處理從 SignalR Service 傳送至用戶端之訊息的一或多個函式。

交涉函式

用戶端應用程式需要有效的存取令牌才能連線到 Azure SignalR 服務。 存取令牌可以是匿名或向使用者標識碼驗證。 無伺服器 SignalR 服務應用程式需要名為 negotiate 的 HTTP 端點,才能取得令牌和其他連線資訊,例如 SignalR 服務端點 URL。

使用 HTTP 觸發的 Azure 函式和 SignalRConnectionInfo 輸入系結來產生連線信息物件。 函式必須具有以 結尾的 /negotiateHTTP 路由。

C# 中使用類別型模型 時,您不需要 SignalRConnectionInfo 輸入系結,而且可以更輕鬆地新增自定義宣告。 如需詳細資訊,請參閱 以類別為基礎的模型交涉體驗。

如需函式 negotiate 的詳細資訊,請參閱 Azure Functions 開發

若要瞭解如何建立已驗證的令牌,請參閱 使用App Service驗證

處理從 SignalR 服務傳送的訊息

使用系 SignalRTrigger 結來處理從 SignalR Service 傳送的訊息。 您可以在用戶端傳送訊息或用戶端連線或中斷連線時收到通知。

如需詳細資訊,請參閱 SignalR Service 觸發程式系結參考

您也需要將函式端點設定為上游端點,以便服務在用戶端有訊息時觸發函式。 如需如何設定上游端點的詳細資訊,請參閱 上游端點

注意

SignalR Service 不支援 StreamInvocation 來自無伺服器模式之用戶端的訊息。

傳送訊息和管理群組成員資格

SignalR使用輸出系結將訊息傳送至連線至 Azure SignalR 服務的用戶端。 您可以將訊息廣播給所有用戶端,也可以將它們傳送至用戶端的子集。 例如,只將訊息傳送給使用特定使用者標識碼驗證的用戶端,或只傳送至特定群組。

用戶可以新增至一或多個群組。 您也可以使用 SignalR 輸出系結,在群組中新增或移除使用者。

如需詳細資訊,請參閱 SignalR 輸出系結參考

SignalR 中樞

SignalR 具有中樞的概念。 每個用戶端連線和從 Azure Functions 傳送的每個訊息都會限定在特定中樞。 您可以使用中樞作為將連線和訊息分隔成邏輯命名空間的方式。

類別型模型

以類別為基礎的模型專用於 C#。

以類別為基礎的模型提供更佳的程式設計體驗,其可以使用下列功能取代 SignalR 輸入和輸出系結:

  • 更有彈性的交涉、傳送訊息和管理群組體驗。
  • 支援更多管理功能,包括關閉連線、檢查連線、使用者或群組是否存在。
  • 強型別中樞
  • 統一中樞名稱和 連接字串 一個位置設定。

下列程式代碼示範如何在類別型模型中撰寫 SignalR 系結:

首先,定義衍生自 類別 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);
    }
}

在 Program.cs 檔案中,註冊無伺服器中樞:

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

以類別為基礎的模型交涉體驗

除了使用 SignalR 輸入系結 [SignalRConnectionInfoInput],在類別型模型中進行交涉可以更有彈性。 基類 ServerlessHub 具有 方法 NegotiateAsync,可讓使用者自定義交涉選項,例如 userIdclaims等。

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

在類別型模型中傳送訊息和管理體驗

您可以存取基類 ServerlessHub所提供的成員,來傳送訊息、管理群組或管理用戶端。

  • ServerlessHub.Clients 用於將訊息傳送至用戶端。
  • ServerlessHub.Groups 用於管理群組的連線,例如將連線新增至群組,從群組移除連線。
  • ServerlessHub.UserGroups 用於管理具有群組的使用者,例如將使用者新增至群組、從群組中移除使用者。
  • ServerlessHub.ClientManager 用於檢查連線是否存在、關閉連線等等。

強型別中樞

強型別中樞 可讓您在將訊息傳送至用戶端時使用強型別方法。 若要在類別型模型中使用強型別中樞,請將用戶端方法擷取至 介面 T,並讓您的中樞類別衍生自 ServerlessHub<T>

下列程式代碼是用戶端方法的介面範例。

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

然後您可以使用強型別方法,如下所示:

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

注意

您可以從 GitHub 取得完整的項目範例

一個位置的整合中樞名稱和 連接字串 設定

  • 沒有伺服器中樞的類別名稱會自動做為 HubName
  • 您可能已經注意到 SignalRConnection 無伺服器中樞類別上使用的屬性,如下所示:
    [SignalRConnection("AzureSignalRConnectionString")]
    public class Functions : ServerlessHub<IChatClient>
    
    它可讓您自定義無伺服器中樞 連接字串 的位置。 如果不存在,則會使用預設值 AzureSignalRConnectionString

重要

SignalR 觸發程式和無伺服器中樞是獨立的。 因此,無伺服器中樞和 SignalRConnection 屬性的類別名稱不會變更 SignalR 觸發程式的設定,即使您在無伺服器中樞內使用 SignalR 觸發程式也一樣。

客戶端開發

SignalR 用戶端應用程式可以使用數種語言之一的 SignalR 用戶端 SDK,輕鬆地連線及接收來自 Azure SignalR 服務的訊息。

設定客戶端連線

若要連線到 SignalR 服務,客戶端必須完成由下列步驟組成的成功連線交涉:

  1. 對上述 HTTP 端點提出要求, negotiate 以取得有效的連線資訊
  2. 使用從端點取得negotiate的服務端點 URL 和存取令牌,連線 至 SignalR Service

SignalR 用戶端 SDK 已經包含執行交涉交握所需的邏輯。 將交涉端點的網址減去 negotiate 區段傳遞至 SDK 的 HubConnectionBuilder。 以下是 JavaScript 中的範例:

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

根據慣例,SDK 會自動附加 /negotiate 至 URL,並用它來開始交涉。

注意

如果您在瀏覽器中使用 JavaScript/TypeScript SDK,則必須在函式應用程式上啟用跨原始來源資源分享 (CORS)。

如需如何使用 SignalR 用戶端 SDK 的詳細資訊,請參閱您的語言檔:

將訊息從用戶端傳送至服務

如果您已 針對 SignalR 資源設定上游 ,您可以使用任何 SignalR 用戶端,將訊息從用戶端傳送至 Azure Functions。 以下是 JavaScript 中的範例:

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

Azure Functions 組態

與 Azure SignalR Service 整合的 Azure 函式應用程式可以像任何一般 Azure 函式應用程式一樣部署,使用持續部署zip 部署,以及從套件執行等技術。

不過,對於使用 SignalR Service 系結的應用程式,有幾個特殊考慮。 如果客戶端在瀏覽器中執行,則必須啟用 CORS。 如果應用程式需要驗證,您可以整合交涉端點與 App Service 驗證。

啟用 CORS

JavaScript/TypeScript 用戶端會向交涉函式提出 HTTP 要求,以起始連線交涉。 當用戶端應用程式裝載於與 Azure Function 應用程式不同的網域時,必須在函式應用程式上啟用跨原始來源資源分享 (CORS),否則瀏覽器會封鎖要求。

本地 主機

在本機計算機上執行函式應用程式時,您可以將區 Host 段新增至 local.settings.json ,以啟用 CORS。 在區 Host 段中,新增兩個屬性:

  • CORS - 輸入基底 URL,這是用戶端應用程式的來源
  • CORSCredentials - 將它設定為 true 以允許 「withCredentials」 要求

範例:

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

雲端 - Azure Functions CORS

若要在 Azure 函式應用程式上啟用 CORS,請移至 Azure 入口網站 中函式應用程式的 [平臺功能] 索引卷標底下的 CORS 組態畫面。

注意

Azure Functions Linux 取用方案中尚未提供 CORS 組態。 使用 Azure API 管理 來啟用 CORS。

必須啟用具有 Access-Control-Allow-Credentials 的 CORS,SignalR 用戶端才能呼叫交涉函式。 若要啟用它,請選取複選框。

在 [ 允許的來源 ] 區段中,新增具有 Web 應用程式原始基底 URL 的專案。

設定 CORS

雲端 - Azure API 管理

Azure API 管理 提供 API 閘道,可將功能新增至現有的後端服務。 您可以使用它將 CORS 新增至函式應用程式。 它提供使用量層,其中包含按動作付費定價和每月免費授與。

如需如何匯入 Azure 函式應用程式的資訊,請參閱 API 管理 檔。 匯入之後,您可以新增輸入原則,以啟用具有 Access-Control-Allow-Credentials 支援的 CORS。

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

將您的 SignalR 用戶端設定為使用 API 管理 URL。

使用 App Service 驗證

Azure Functions 具有內建驗證,支援熱門提供者,例如 Facebook、Twitter、Microsoft 帳戶、Google 和 Microsoft Entra ID。 這項功能可以與系結整合, SignalRConnectionInfo 以建立已向使用者標識符驗證的 Azure SignalR Service 連線。 您的應用程式可以使用以該使用者標識碼為目標的輸出系結來傳送訊息 SignalR

在 [Azure 入口網站] 的 [函式應用程式平臺功能] 索引卷標中,開啟 [驗證/授權設定] 視窗。 請遵循 App Service 驗證的檔,以使用您選擇的識別提供者來設定驗證。

設定之後,已驗證的 HTTP 要求會分別包含 x-ms-client-principal-name 包含 x-ms-client-principal-id 已驗證身分識別使用者名稱和使用者標識碼的標頭。

您可以在系 SignalRConnectionInfo 結組態中使用這些標頭來建立已驗證的連線。 以下是使用標頭的 x-ms-client-principal-id C# 交涉函式範例。

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

然後,您可以藉由設定 UserId 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 }
        });
}

如需其他語言的相關信息,請參閱 Azure Functions 的 Azure SignalR 服務系結 參考。

下一步

在本文中,您將瞭解如何使用 Azure Functions 開發及設定無伺服器 SignalR Service 應用程式。 請嘗試使用 SignalR Service 概觀頁面上的其中一個快速入門或教學課程自行建立應用程式。