分享方式:


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

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

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

SignalR Service 組態

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

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

SignalR Service 模式

Azure Functions 開發

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

  • 用戶端會呼叫「negotiate」函式來取得有效的 SignalR Service 存取權杖和端點 URL。
  • 處理從 SignalR Service 傳送至用戶端之訊息的一或多個函式。

交涉函式

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

使用 HTTP 觸發的 Azure 函式和 SignalRConnectionInfo 輸入繫結來產生連線資訊物件。 函式必須有以 /negotiate 結尾的 HTTP 路由。

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

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

如要了解如何建立驗證權杖,請參閱使用 App Service 驗證

處理來自 SignalR Service 的訊息

使用 SignalRTrigger 繫結來處理來自 SignalR Service 的訊息。 當用戶端傳送訊息,或是用戶端連線或中斷連線時,您就會收到通知。

如需詳細資訊,請參閱 SignalR Service 觸發繫結參考資料

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

注意

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

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

使用 SignalR 輸出繫結將訊息傳送到連線至 Azure SignalR Service 的用戶端。 您可以將訊息廣播到所有用戶端,或是傳送到用戶端子集。 例如只傳送訊息到使用特定使用者識別碼驗證的用戶端,或只傳送訊息到特定群組。

使用者可以加入一或多個群組。 您也可以使用 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 Service 的訊息。

設定用戶端連線

如要連線至 SignalR Service,用戶端必須完成包含下列步驟的成功連線交涉:

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

SignalR 用戶端 SDK 已經包含執行交涉交握所需的邏輯。 將交涉端點的 URL 減去 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 函式應用程式可以使用持續部署zip 部署從套件執行等技術,像任何典型的 Azure 函式應用程式一樣進行部署。

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

啟用 CORS

JavaScript/TypeScript 用戶端會對交涉函式提出 HTTP 要求,以起始連線交涉。 當用戶端應用程式託管於與 Azure Function 應用程式不同的網域時,必須在函式應用程式上啟用 CORS,否則瀏覽器將封鎖要求。

Localhost

在您的本機電腦上執行函式應用程式時,您可以新增 [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 用戶端才能呼叫交涉函式。 若要啟用,請選取核取方塊。

在 [允許的來源] 區段中,新增具有您網頁應用程式來源基底 URL 的項目。

設定 CORS

雲端 - Azure API 管理

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

請參閱 API 管理文件,了解如何匯入 Azure 函式應用程式的詳細資訊。 匯入後,您可以新增輸入政策,以啟用具有 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、X、Microsoft 帳戶、Google 和 Microsoft Entra ID。 此功能可以與 SignalRConnectionInfo 繫結整合,以與已驗證至使用者識別碼的 Azure SignalR Service 建立連線。 您的應用程式可以使用以該使用者識別碼為目標的 SignalR 輸出繫結來傳送訊息。

在 Azure 入口網站中,前往函式應用程式的 [平臺功能] 索引標籤中,開啟 [驗證/授權設定] 視窗。 按照 App Service 驗證文件的說明,使用您選擇的身分識別提供者設定驗證。

設定完成後,已驗證的 HTTP 要求會包含 x-ms-client-principal-namex-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;
}

接著,您可以透過設定 SignalR 訊息的 UserId 屬性,將訊息傳送給該使用者。

[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 SignalR Service 繫結,了解 Azure Functions 參考資料。

下一步

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