适用于 Azure Functions 的 Azure 事件网格输出绑定

使用事件网格输出绑定将事件写入到自定义主题。 必须拥有有效的自定义主题访问密钥。 事件网格输出绑定不支持共享访问签名 (SAS) 令牌。

若要了解设置和配置详细信息,请参阅如何在 Azure Functions 中使用事件网格触发器和绑定

重要

本文使用选项卡来支持多个版本的 Node.js 编程模型。 v4 模型目前处于预览状态,旨在为 JavaScript 和 TypeScript 开发人员提供更为灵活和直观的体验。 在升级指南中详细了解 v3 和 v4 之间的差异。

Azure Functions 支持两种 Python 编程模型。 定义绑定的方式取决于选择的编程模型。

使用 Python v2 编程模型,可以直接在 Python 函数代码中使用修饰器定义绑定。 有关详细信息,请参阅 Python 开发人员指南

本文同时支持两个编程模型。

重要

事件网格输出绑定仅适用于 Functions 2.x 和更高版本。

示例

与事件网格输出绑定一起使用的输出参数的类型取决于 Functions 运行时版本、绑定扩展版本和 C# 函数的形式。 可以使用以下 C# 模式之一创建 C# 函数:

  • 进程内类库:编译的 C# 函数,该函数在与 Functions 运行时相同的进程中运行。
  • 独立工作进程类库:编译的 C# 函数,该函数在独立于运行时的工作进程中运行。

下面的示例演示如何在触发器和事件网格输出绑定中使用自定义类型:

using System;
using System.Collections.Generic;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace SampleApp
{
    public static class EventGridFunction
    {
        [Function(nameof(EventGridFunction))]
        [EventGridOutput(TopicEndpointUri = "MyEventGridTopicUriSetting", TopicKeySetting = "MyEventGridTopicKeySetting")]
        public static MyEventType Run([EventGridTrigger] MyEventType input, FunctionContext context)
        {
            var logger = context.GetLogger(nameof(EventGridFunction));

            logger.LogInformation(input.Data.ToString());

            var outputEvent = new MyEventType()
            {
                Id = "unique-id",
                Subject = "abc-subject",
                Data = new Dictionary<string, object>
                {
                    { "myKey", "myValue" }
                }
            };

            return outputEvent;
        }
    }

    public class MyEventType
    {
        public string Id { get; set; }

        public string Topic { get; set; }

        public string Subject { get; set; }

        public string EventType { get; set; }

        public DateTime EventTime { get; set; }

        public IDictionary<string, object> Data { get; set; }
    }
}

以下示例显示了一个将消息写入事件网格自定义主题的 Java 函数。 此函数使用绑定的 setValue 方法输出字符串。

public class Function {
    @FunctionName("EventGridTriggerTest")
    public void run(@EventGridTrigger(name = "event") String content,
            @EventGridOutput(name = "outputEvent", topicEndpointUri = "MyEventGridTopicUriSetting", topicKeySetting = "MyEventGridTopicKeySetting") OutputBinding<String> outputEvent,
            final ExecutionContext context) {
        context.getLogger().info("Java EventGrid trigger processed a request." + content);
        final String eventGridOutputDocument = "{\"id\": \"1807\", \"eventType\": \"recordInserted\", \"subject\": \"myapp/cars/java\", \"eventTime\":\"2017-08-10T21:03:07+00:00\", \"data\": {\"make\": \"Ducati\",\"model\": \"Monster\"}, \"dataVersion\": \"1.0\"}";
        outputEvent.setValue(eventGridOutputDocument);
    }
}

你还可使用 POJO 类发送事件网格消息。

public class Function {
    @FunctionName("EventGridTriggerTest")
    public void run(@EventGridTrigger(name = "event") String content,
            @EventGridOutput(name = "outputEvent", topicEndpointUri = "MyEventGridTopicUriSetting", topicKeySetting = "MyEventGridTopicKeySetting") OutputBinding<EventGridEvent> outputEvent,
            final ExecutionContext context) {
        context.getLogger().info("Java EventGrid trigger processed a request." + content);

        final EventGridEvent eventGridOutputDocument = new EventGridEvent();
        eventGridOutputDocument.setId("1807");
        eventGridOutputDocument.setEventType("recordInserted");
        eventGridOutputDocument.setEventTime("2017-08-10T21:03:07+00:00");
        eventGridOutputDocument.setDataVersion("1.0");
        eventGridOutputDocument.setSubject("myapp/cars/java");
        eventGridOutputDocument.setData("{\"make\": \"Ducati\",\"model\":\"monster\"");

        outputEvent.setValue(eventGridOutputDocument);
    }
}

class EventGridEvent {
    private String id;
    private String eventType;
    private String subject;
    private String eventTime;
    private String dataVersion;
    private String data;

    public String getId() {
        return id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getDataVersion() {
        return dataVersion;
    }

    public void setDataVersion(String dataVersion) {
        this.dataVersion = dataVersion;
    }

    public String getEventTime() {
        return eventTime;
    }

    public void setEventTime(String eventTime) {
        this.eventTime = eventTime;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getEventType() {
        return eventType;
    }

    public void setEventType(String eventType) {
        this.eventType = eventType;
    }

    public void setId(String id) {
        this.id = id;
    }  
}

以下示例展示了一个计时器触发的 TypeScript 函数,它会输出单个事件:

import { app, EventGridPartialEvent, InvocationContext, output, Timer } from '@azure/functions';

export async function timerTrigger1(myTimer: Timer, context: InvocationContext): Promise<EventGridPartialEvent> {
    const timeStamp = new Date().toISOString();
    return {
        id: 'message-id',
        subject: 'subject-name',
        dataVersion: '1.0',
        eventType: 'event-type',
        data: {
            name: 'John Henry',
        },
        eventTime: timeStamp,
    };
}

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.eventGrid({
        topicEndpointUri: 'MyEventGridTopicUriSetting',
        topicKeySetting: 'MyEventGridTopicKeySetting',
    }),
    handler: timerTrigger1,
});

要输出多个事件,请返回一个数组而不是单个对象。 例如:

import { app, EventGridPartialEvent, InvocationContext, output, Timer } from '@azure/functions';

export async function timerTrigger1(myTimer: Timer, context: InvocationContext): Promise<EventGridPartialEvent[]> {
    // <displayInDocs>
    const timeStamp = new Date().toISOString();
    return [
        {
            id: 'message-id',
            subject: 'subject-name',
            dataVersion: '1.0',
            eventType: 'event-type',
            data: {
                name: 'John Henry',
            },
            eventTime: timeStamp,
        },
        {
            id: 'message-id-2',
            subject: 'subject-name',
            dataVersion: '1.0',
            eventType: 'event-type',
            data: {
                name: 'John Doe',
            },
            eventTime: timeStamp,
        },
    ];
    // </displayInDocs>
}

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.eventGrid({
        topicEndpointUri: 'MyEventGridTopicUriSetting',
        topicKeySetting: 'MyEventGridTopicKeySetting',
    }),
    handler: timerTrigger1,
});

以下示例展示了一个计时器触发的 JavaScript 函数,它会输出单个事件:

const { app, output } = require('@azure/functions');

const eventGridOutput = output.eventGrid({
    topicEndpointUri: 'MyEventGridTopicUriSetting',
    topicKeySetting: 'MyEventGridTopicKeySetting',
});

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: eventGridOutput,
    handler: (myTimer, context) => {
        const timeStamp = new Date().toISOString();
        return {
            id: 'message-id',
            subject: 'subject-name',
            dataVersion: '1.0',
            eventType: 'event-type',
            data: {
                name: 'John Henry',
            },
            eventTime: timeStamp,
        };
    },
});

要输出多个事件,请返回一个数组而不是单个对象。 例如:

const { app, output } = require('@azure/functions');

const eventGridOutput = output.eventGrid({
    topicEndpointUri: 'MyEventGridTopicUriSetting',
    topicKeySetting: 'MyEventGridTopicKeySetting',
});

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: eventGridOutput,
    handler: (myTimer, context) => {
        // <displayInDocs>
        const timeStamp = new Date().toISOString();
        return [
            {
                id: 'message-id',
                subject: 'subject-name',
                dataVersion: '1.0',
                eventType: 'event-type',
                data: {
                    name: 'John Henry',
                },
                eventTime: timeStamp,
            },
            {
                id: 'message-id-2',
                subject: 'subject-name',
                dataVersion: '1.0',
                eventType: 'event-type',
                data: {
                    name: 'John Doe',
                },
                eventTime: timeStamp,
            },
        ];
        // </displayInDocs>
    },
});

下面的示例演示如何配置函数以输出事件网格事件消息。 type 设置为 eventGrid 的部分配置建立事件网格输出绑定所需的值。

{
  "bindings": [
    {
      "type": "eventGrid",
      "name": "outputEvent",
      "topicEndpointUri": "MyEventGridTopicUriSetting",
      "topicKeySetting": "MyEventGridTopicKeySetting",
      "direction": "out"
    },
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    }
  ]
}

在函数中,请使用 Push-OutputBinding 通过事件网格输出绑定将事件发送到自定义主题。

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$message = $Request.Query.Message

Push-OutputBinding -Name outputEvent -Value  @{
    id = "1"
    eventType = "testEvent"
    subject = "testapp/testPublish"
    eventTime = "2020-08-27T21:03:07+00:00"
    data = @{
        Message = $message
    }
    dataVersion = "1.0"
}

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = 200
    Body = "OK"
})

以下示例演示了触发器绑定以及使用该绑定的 Python 函数。 然后,它会将事件发送到自定义主题,如 topicEndpointUri 所指定。 该示例取决于使用的是 v1 还是 v2 Python 编程模型

下面是 function_app.py 文件中的函数:

import logging
import azure.functions as func
import datetime

app = func.FunctionApp()

@app.function_name(name="eventgrid_output")
@app.event_grid_trigger(arg_name="eventGridEvent")
@app.event_grid_output(
    arg_name="outputEvent",
    topic_endpoint_uri="MyEventGridTopicUriSetting",
    topic_key_setting="MyEventGridTopicKeySetting")
def eventgrid_output(eventGridEvent: func.EventGridEvent, 
         outputEvent: func.Out[func.EventGridOutputEvent]) -> None:

    logging.log("eventGridEvent: ", eventGridEvent)

    outputEvent.set(
        func.EventGridOutputEvent(
            id="test-id",
            data={"tag1": "value1", "tag2": "value2"},
            subject="test-subject",
            event_type="test-event-1",
            event_time=datetime.datetime.utcnow(),
            data_version="1.0"))

特性

进程内独立工作进程 C# 库使用特性来配置绑定。 C# 脚本改用 function.json 配置文件,如 C# 脚本指南中所述。

该特性的构造函数可接受包含自定义主题名称的应用程序设置的名称,以及包含主题密钥的应用程序设置的名称。

下表说明了 EventGridOutputAttribute 的参数。

参数 说明
TopicEndpointUri 包含自定义主题 URI 的应用设置的名称,例如 MyTopicEndpointUri
TopicKeySetting 包含自定义主题访问密钥的应用设置的名称。
连接 包含主题终结点 URI 的设置的常见前缀的值。 有关此应用程序设置的命名格式的详细信息,请参阅基于标识的身份验证

批注

对于 Java 类,使用 EventGridAttribute 特性。

该特性的构造函数可接受包含自定义主题名称的应用设置的名称,以及包含主题密钥的应用设置的名称。 有关这些设置的详细信息,请参阅输出 - 配置。 下面是 EventGridOutput 特性的示例:

public class Function {
    @FunctionName("EventGridTriggerTest")
    public void run(@EventGridTrigger(name = "event") String content,
            @EventGridOutput(name = "outputEvent", topicEndpointUri = "MyEventGridTopicUriSetting", topicKeySetting = "MyEventGridTopicKeySetting") OutputBinding<String> outputEvent, final ExecutionContext context) {
            ...
    }
}

配置

下表说明了可以在传递给 output.eventGrid() 方法的 options 对象上设置的属性。

properties 说明
topicEndpointUri 包含自定义主题 URI 的应用设置的名称,例如 MyTopicEndpointUri
topicKeySetting 包含自定义主题访问密钥的应用设置的名称。
连接 包含主题终结点 URI 的设置的常见前缀的值。 设置 connection 属性时,不应设置 topicEndpointUritopicKeySetting 属性。 有关此应用程序设置的命名格式的详细信息,请参阅基于标识的身份验证

配置

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

“function.json”属性 说明
type 必须设置为 eventGrid
direction 必须设置为 out。 在 Azure 门户中创建绑定时,会自动设置该参数。
name 函数代码中使用的表示事件的变量名称。
topicEndpointUri 包含自定义主题 URI 的应用设置的名称,例如 MyTopicEndpointUri
topicKeySetting 包含自定义主题访问密钥的应用设置的名称。
连接 包含主题终结点 URI 的设置的常见前缀的值。 有关此应用程序设置的命名格式的详细信息,请参阅基于标识的身份验证

*支持基于标识的连接需要该扩展的 3.3.x 或更高版本。

在本地开发时,请将应用程序设置添加到 Values 集合的 local.settings.json 文件

重要

请确保将 TopicEndpointUri 的值设置为包含自定义主题 URI 的应用设置的名称。 请勿直接在此属性中指定自定义主题的 URI。 使用 Connection 时同样适用。

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

使用情况

事件网格输出绑定支持的参数类型取决于 Functions 运行时版本、扩展包版本以及使用的 C# 形式。

如果希望函数写入单个事件,事件网格输出绑定可以绑定到以下类型:

类型 说明
string 字符串形式的事件。
byte[] 事件消息的字节数。
JSON 可序列化类型 表示 JSON 事件的对象。 Functions 会尝试将普通的旧 CLR 对象 (POCO) 类型序列化为 JSON 数据。

如果希望函数写入多个事件,事件网格输出绑定可以绑定到以下类型:

类型 说明
T[],其中 T 是单事件类型之一 包含多个事件的数组。 每个条目表示一个事件。

对于其他输出方案,请直接创建和使用 Azure.Messaging.EventGrid 中的类型。

通过调用方法参数(如 out EventGridOutput paramName)发送单个消息,通过 ICollector<EventGridOutput> 编写多个消息。

通过直接返回值或使用 context.extraOutputs.set() 来访问输出消息。

通过使用 Push-OutputBinding cmdlet 访问输出事件,将事件发送到事件网格输出绑定。

有两个选项可用于通过函数输出事件网格消息:

  • 返回值:将 function.json 中的 name 属性 设置为 $return。 使用此配置时,函数的返回值将作为事件网格消息保留。
  • 命令性:将值传递给声明为 Out 类型的参数的 set 方法。 传递给 set 的值将作为事件网格消息保留。

连接

使用事件网格输出绑定时,可通过两种方式向事件网格主题进行身份验证:

身份验证方法 说明
使用主题密钥 设置 TopicEndpointUriTopicKeySetting 属性,如使用主题密钥中所述。
使用标识 Connection 属性设置为多个应用程序设置的共享前缀的名称,这些设置共同定义基于标识的身份验证。 使用 3.3.x 或更高版本的扩展时,支持此方法。

使用主题密钥

使用以下步骤配置主题密钥:

  1. 按照获取访问密钥中的步骤获取事件网格主题的主题密钥。

  2. 在应用程序设置中,创建定义主题密钥值的设置。 将此设置的名称用于绑定的 TopicKeySetting 属性。

  3. 在应用程序设置中,创建定义主题终结点的设置。 将此设置的名称用于绑定的 TopicEndpointUri 属性。

基于标识的身份验证

使用 3.3.x 或更高版本的扩展时,可使用 Microsoft Entra 标识连接到事件网格主题,以避免获取和使用主题密钥。

需要创建一个返回主题终结点 URI 的应用程序设置。 设置的名称应将唯一通用前缀(例如 myawesometopic)与值 __topicEndpointUri 组合在一起。 然后,在绑定中定义 Connection 属性时,必须使用该通用前缀(在本例中为 myawesometopic)。

在此模式下,扩展需要以下属性:

属性 环境变量模板 说明 示例值
主题终结点 URI <CONNECTION_NAME_PREFIX>__topicEndpointUri 主题终结点。 https://<topic-name>.centralus-1.eventgrid.chinacloudapi.cn/api/events

可以使用更多属性来自定义连接。 请参阅基于标识的连接的通用属性

注意

使用 Azure 应用程序配置Key Vault 来为基于托管标识的连接提供设置时,设置名称应使用有效的密钥分隔符(例如 :/)来代替 __,从而确保名称得到正确解析。

例如 <CONNECTION_NAME_PREFIX>:topicEndpointUri

在 Azure Functions 服务中托管时,基于标识的连接将使用托管标识。 默认情况下使用系统分配的标识,但可以使用 credentialclientID 属性来指定用户分配的标识。 请注意,不支持为用户分配的标识配置资源 ID。 在其他上下文(如本地开发)中运行时,将改用开发人员标识,尽管可以进行自定义。 请参阅使用基于标识的连接进行本地开发

向标识授予权限

无论使用何种标识,都必须具有执行所需操作的权限。 需要使用内置角色或者提供这些权限的自定义角色在 Azure RBAC 中分配角色

重要

某些权限可能由并非所有上下文都需要的目标服务公开。 尽可能遵循最低权限原则,仅授予标识所需的权限。 例如,如果应用只需要从数据源进行读取即可,则使用仅具有读取权限的角色。 分配一个也具有该服务写入权限的角色并不恰当,因为对于读取操作来说,写入是多余的权限。 同样,你也希望确保角色分配的范围仅限于需要读取的资源。

必须创建一个角色分配,以便在运行时提供对事件网格主题的访问权限。 所有者等管理角色还不够。 下表显示了在正常操作中使用事件中心扩展时建议使用的内置角色。 根据所编写的代码,应用程序可能需要具有其他权限。

绑定类型 内置角色示例
输出绑定 EventGrid 参与者EventGrid 数据发送方

后续步骤