Compartir a través de

Azure Functions 的 SignalR 服务触发器绑定

使用 SignalR 触发器绑定来响应从 Azure SignalR 服务发送的消息。 触发函数时,传递给函数的消息分析为 json 对象。

在 SignalR 服务无服务器模式下,SignalR 服务使用上游功能从客户端向函数应用发送消息。 函数应用会使用 SignalR 服务触发器绑定来处理这些消息。 一般的体系结构如下所示:

SignalR 触发器体系结构

若要了解设置和配置详细信息,请参阅概述

示例

可使用以下 C# 模式之一来创建 C# 函数:

  • 进程内类库:编译的 C# 函数,该函数在与 Functions 运行时相同的进程中运行。
  • 独立工作进程类库:编译的 C# 函数,该函数在独立于运行时的工作进程中运行。 需要独立工作进程才能支持在 LTS 和非 LTS 版 .NET 和 .NET Framework 上运行的 C# 函数。
  • C# 脚本:主要在 Azure 门户中创建 C# 函数时使用。

以下示例演示一个 C# 函数,该函数从客户端接收消息事件并记录消息内容。

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.


using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace Extensions.SignalR
{
    public static class SignalRTriggerFunctions
    {
        // <snippet_on_connected>
        [Function(nameof(OnConnected))]
        public static void OnConnected(
            [SignalRTrigger("chat", "connections", "connected", ConnectionStringSetting = "SignalRConnection")]
                SignalRInvocationContext invocationContext, FunctionContext functionContext)
        {
            var logger = functionContext.GetLogger(nameof(OnConnected));
            logger.LogInformation("Connection {connectionId} is connected", invocationContext.ConnectionId);
        }
        // </snippet_on_connected>

        // <snippet_on_disconnected>
        [Function(nameof(OnDisconnected))]
        public static void OnDisconnected(
            [SignalRTrigger("chat", "connections", "disconnected", ConnectionStringSetting = "SignalRConnection")]
                SignalRInvocationContext invocationContext, FunctionContext functionContext)
        {
            var logger = functionContext.GetLogger(nameof(OnDisconnected));
            logger.LogInformation("Connection {connectionId} is disconnected. Error: {error}", invocationContext.ConnectionId, invocationContext.Error);
        }
        // </snippet_on_disconnected>

        // <snippet_on_message>
        [Function(nameof(OnClientMessage))]
        public static void OnClientMessage(
            [SignalRTrigger("Hub", "messages", "sendMessage", "content", ConnectionStringSetting = "SignalRConnection")]
                SignalRInvocationContext invocationContext, string content, FunctionContext functionContext)
        {
            var logger = functionContext.GetLogger(nameof(OnClientMessage));
            logger.LogInformation("Connection {connectionId} sent a message. Message content: {content}", invocationContext.ConnectionId, content);
        }
        // </snippet_on_message>
    }
}

重要

由于 C# 辅助角色模型的限制,C# 独立辅助角色中 SignalR 服务绑定的基于类的模型无法优化你编写 SignalR 触发器的方式。 有关基于类的模型的详细信息,请参阅基于类的模型

Java 目前不支持 SignalR 触发器。

下面是 function.json 文件中的绑定数据:

{
    "type": "signalRTrigger",
    "name": "invocation",
    "hubName": "hubName1",
    "category": "messages",
    "event": "SendMessage",
    "parameterNames": [
        "message"
    ],
    "direction": "in"
}
app.generic("function1",
    {
        trigger: { "type": "signalRTrigger", "name": "invocation", "direction": "in", "hubName": "hubName1", "event": "SendMessage", "category": "messages" },
        handler: (triggerInput, context) => {
            context.log(`Receive ${context.Arguments[0]} from ${triggerInput.ConnectionId}.`)
        }
    })

完整的 PowerShell 示例处于待定状态。

下面是 Python 代码:

import logging
import json
import azure.functions as func

def main(invocation) -> None:
    invocation_json = json.loads(invocation)
    logging.info("Receive {0} from {1}".format(invocation_json['Arguments'][0], invocation_json['ConnectionId']))

特性

进程内独立工作进程 C# 库都使用 SignalRTrigger 特性来定义函数。 C# 脚本改为使用 function.json 配置文件。

下表说明了 SignalRTrigger 特性的属性。

Attribute 属性 说明
HubName 此值必须设置为要触发的函数的 SignalR 中心的名称。
类别 此值必须设置为要触发的函数的消息类别。 类别可以是下列值之一:
  • 连接:包括“已连接”和“已断开连接”的事件
  • 消息:包含除连接类别中事件的所有其他事件
事件 此值必须设置为要触发的函数的消息事件。 对于消息类别,事件是客户端发送的调用消息中的目标 。 对于连接类别,只使用“已连接”和“已断开连接” 。
ParameterNames (可选)绑定到参数的名称列表。
ConnectionStringSetting 包含 SignalR 服务连接字符串(默认为 AzureSignalRConnectionString)的应用设置的名称。

批注

对于 SignalR 触发器,当前还没有受支持的 Java 注释。

配置

下表解释了在 function.json 文件中设置的绑定配置属性。

“function.json”属性 说明
type 必须设置为 SignalRTrigger
direction 必须设置为 in
name 在函数代码中用于“触发器调用上下文”对象的变量名称。
hubName 此值必须设置为要触发的函数的 SignalR 中心的名称。
category 此值必须设置为要触发的函数的消息类别。 类别可以是下列值之一:
  • 连接:包括“已连接”和“已断开连接”的事件
  • 消息:包含除连接类别中事件的所有其他事件
event 此值必须设置为要触发的函数的消息事件。 对于消息类别,事件是客户端发送的调用消息中的目标 。 对于连接类别,只使用“已连接”和“已断开连接” 。
parameterNames (可选)绑定到参数的名称列表。
connectionStringSetting 包含 SignalR 服务连接字符串(默认为 AzureSignalRConnectionString)的应用设置的名称。

有关完整示例,请参阅示例部分

使用情况

Payloads

触发器输入类型声明为 InvocationContext 或自定义类型。 如果选择 InvocationContext,会获得对请求内容的完全访问权限。 对于自定义类型,运行时会尝试分析 JSON 请求正文,以设置对象属性。

InvocationContext

InvocationContext 包含从 SignalR 服务发送的消息中的所有内容,其中包括以下属性:

属性 说明
参数 可用于消息类别。 包含调用消息中的参数
错误 可用于“已断开连接”事件。 如果连接关闭时未发生错误,可以为空,否则会包含错误消息。
Hub 消息所属的中心的名称。
类别 消息的类别。
事件 消息的事件。
ConnectionId 发送消息的客户端的连接 ID。
UserID 发送消息的客户端的用户标识。
标头 请求的标头。
查询 客户端连接到服务时的请求的查询。
声明 客户端的声明。

使用 ParameterNames

通过 SignalRTrigger 中的属性 ParameterNames,可将调用消息的参数绑定到函数的参数。 定义的名称可在其他绑定中用作绑定表达式的一部分,或者用作代码中的参数。 这为你提供了更方便的方法来访问 InvocationContext 的参数。

假设你有 JavaScript SignalR 客户端尝试使用两个参数 message1message2 调用 Azure Function 中的方法 broadcast

await connection.invoke("broadcast", message1, message2);

设置 parameterNames 后,定义的名称将对应于客户端发送的参数。

[SignalRTrigger(parameterNames: new string[] {"arg1, arg2"})]

然后,arg1 包含 message1 的内容,arg2 包含 message2 的内容。

ParameterNames 注意事项

对于参数绑定,顺序很重要。 如果使用 ParameterNames,则 ParameterNames 中的顺序与在客户端中调用参数的顺序相匹配。 如果在 C# 中使用特性 [SignalRParameter],则 Azure Function 方法中的参数顺序与客户端中参数的顺序相匹配。

ParameterNames 和属性 [SignalRParameter] 不能同时使用,否则将出现异常

SignalR 服务集成

当你使用 SignalR 服务触发器绑定时,SignalR 服务需要一个用于访问函数应用的 URL。 应在 SignalR 服务端的上游设置中配置 URL。

上游门户

使用 SignalR 服务触发器时,URL 可以十分简单,其格式可以设置为如下所示:

<Function_App_URL>/runtime/webhooks/signalr?code=<API_KEY>

Function_App_URL 可在函数应用的“概述”页上找到,API_KEY 由 Azure Function 生成。 可以从函数应用的“应用密钥”边栏选项卡中获取 signalr_extensionAPI_KEYAPI 密钥

如果要将多个函数应用与一个 SignalR 服务一起使用,上游还可以支持复杂的路由规则。 有关更多详细信息,请查看上游设置

分步示例

可以按照 GitHub 中的示例,使用 SignalR 服务触发器绑定和上游功能在函数应用上部署聊天室:双向聊天室示例

后续步骤