В этой статье описано, как проверить конечную точку HTTP, чтобы получать события из подписки событий, а затем получать события и десериализировать их. В этой статье используется функция Azure для демонстрационных целей. Однако те же понятия применяются независимо от того, где размещено приложение.
Примечание
Рекомендуется использовать триггер сетки событий при активации функции Azure с сеткой событий. Это обеспечивает более простую и быструю интеграцию между Сеткой событий и Функциями Azure. Однако триггер сетки событий Функции Azure не поддерживает сценарий, в котором размещенный код должен контролировать код состояния HTTP, возвращенный в сетку событий. Учитывая это ограничение, код, выполняющийся в функции Azure, не сможет вернуть ошибку 5XX, чтобы инициировать повторную попытку доставки событий в сетке событий, например.
Необходимые компоненты
Вам потребуется приложение-функция с функцией, активируемой HTTP.
Пакеты SDK для других языков доступны в справочнике по пакетам SDK для публикации. Эти пакеты содержат модели для собственных типов событий, таких как EventGridEvent, StorageBlobCreatedEventData и EventHubCaptureFileCreatedEventData.
Проверка конечной точки
Сначала нужно обработать событие Microsoft.EventGrid.SubscriptionValidationEvent. Каждый раз, когда пользователь подписывается на событие, служба "Сетка событий" отправляет событие проверки в конечную точку с validationCode в полезных данных. Конечная точка необходима для передачи этих данных обратно в тело ответа, чтобы подтвердить, что конечная точка допустима и вы являетесь ее владельцем. При использовании триггера Сетки событий вместо функции, активируемой веб-перехватчиком, проверка конечной точки выполняется автоматически. При использовании службы API сторонних производителей (например, Zapier или IFTTT), возможно, не удастся вывести на экран код проверки программными средствами. Для этих служб можно вручную проверить подписку с помощью URL-адреса проверки, который отправляется в событии проверки подписки. Скопируйте этот URL-адрес в свойство validationUrl и отправьте запрос GET через клиент REST или веб-браузер.
В C# ParseMany() метод используется для десериализации BinaryData экземпляра, содержащего одно или несколько событий в массив EventGridEvent. Если вы знали заранее, что десериализация выполняется только одно событие, вместо этого можно использовать Parse метод.
Чтобы вывести на экран код проверки программными средствами, используйте следующий код:
c#
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using Azure.Messaging.EventGrid;
using Azure.Messaging.EventGrid.SystemEvents;
namespaceFunction1
{
publicstaticclassFunction1
{
[FunctionName("Function1")]
publicstaticasync Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string response = string.Empty;
BinaryData events = await BinaryData.FromStreamAsync(req.Body);
log.LogInformation($"Received events: {events}");
EventGridEvent[] eventGridEvents = EventGridEvent.ParseMany(events);
foreach (EventGridEvent eventGridEvent in eventGridEvents)
{
// Handle system eventsif (eventGridEvent.TryGetSystemEventData(outobject eventData))
{
// Handle the subscription validation eventif (eventData is SubscriptionValidationEventData subscriptionValidationEventData)
{
log.LogInformation($"Got SubscriptionValidation event data, validation code: {subscriptionValidationEventData.ValidationCode}, topic: {eventGridEvent.Topic}");
// Do any additional validation (as required) and then return back the below responsevar responseData = new
{
ValidationResponse = subscriptionValidationEventData.ValidationCode
};
returnnew OkObjectResult(responseData);
}
}
}
returnnew OkObjectResult(response);
}
}
}
JavaScript
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function begun');
var validationEventType = "Microsoft.EventGrid.SubscriptionValidationEvent";
for (var events in req.body) {
var body = req.body[events];
// Deserialize the event data into the appropriate type based on event typeif (body.data && body.eventType == validationEventType) {
context.log("Got SubscriptionValidation event data, validation code: " + body.data.validationCode + " topic: " + body.topic);
// Do any additional validation (as required) and then return back the below responsevar code = body.data.validationCode;
context.res = { status: 200, body: { "ValidationResponse": code } };
}
}
context.done();
};
Проверка тестового ответа
Проверьте функцию ответа проверки, вставив в примере событие в поле проверки функции:
При нажатии кнопки "Выполнить" выходные данные должны быть 200 ОК и {"validationResponse":"512d38b6-c7b8-40c8-89fe-f46f9e9622b6"} в тексте:
Обработка событий хранилища BLOB-объектов
Теперь можно расширить функцию для обработки системного события Microsoft.Storage.BlobCreated:
c#
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using Azure.Messaging.EventGrid;
using Azure.Messaging.EventGrid.SystemEvents;
namespaceFunction1
{
publicstaticclassFunction1
{
[FunctionName("Function1")]
publicstaticasync Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string response = string.Empty;
BinaryData events = await BinaryData.FromStreamAsync(req.Body);
log.LogInformation($"Received events: {events}");
EventGridEvent[] eventGridEvents = EventGridEvent.ParseMany(events);
foreach (EventGridEvent eventGridEvent in eventGridEvents)
{
// Handle system eventsif (eventGridEvent.TryGetSystemEventData(outobject eventData))
{
// Handle the subscription validation eventif (eventData is SubscriptionValidationEventData subscriptionValidationEventData)
{
log.LogInformation($"Got SubscriptionValidation event data, validation code: {subscriptionValidationEventData.ValidationCode}, topic: {eventGridEvent.Topic}");
// Do any additional validation (as required) and then return back the below responsevar responseData = new
{
ValidationResponse = subscriptionValidationEventData.ValidationCode
};
returnnew OkObjectResult(responseData);
}
// Handle the storage blob created eventelseif (eventData is StorageBlobCreatedEventData storageBlobCreatedEventData)
{
log.LogInformation($"Got BlobCreated event data, blob URI {storageBlobCreatedEventData.Url}");
}
}
}
returnnew OkObjectResult(response);
}
}
}
JavaScript
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function begun');
var validationEventType = "Microsoft.EventGrid.SubscriptionValidationEvent";
var storageBlobCreatedEvent = "Microsoft.Storage.BlobCreated";
for (var events in req.body) {
var body = req.body[events];
// Deserialize the event data into the appropriate type based on event type if (body.data && body.eventType == validationEventType) {
context.log("Got SubscriptionValidation event data, validation code: " + body.data.validationCode + " topic: " + body.topic);
// Do any additional validation (as required) and then return back the below responsevar code = body.data.validationCode;
context.res = { status: 200, body: { "ValidationResponse": code } };
}
elseif (body.data && body.eventType == storageBlobCreatedEvent) {
var blobCreatedEventData = body.data;
context.log("Relaying received blob created event payload:" + JSON.stringify(blobCreatedEventData));
}
}
context.done();
};
Обработка события создания тестового большого двоичного объекта
В журнале функции отобразятся выходные данные URL-адреса большого двоичного объекта:
Bash
2022-11-14T22:40:45.978 [Information] Executing 'Function1' (Reason='This function was programmatically called via the host APIs.', Id=8429137d-9245-438c-8206-f9e85ef5dd61)
2022-11-14T22:40:46.012 [Information] C# HTTP trigger function processed a request.
2022-11-14T22:40:46.017 [Information] Received events: [{"topic": "/subscriptions/{subscription-id}/resourceGroups/Storage/providers/Microsoft.Storage/storageAccounts/xstoretestaccount","subject": "/blobServices/default/containers/testcontainer/blobs/testfile.txt","eventType": "Microsoft.Storage.BlobCreated","eventTime": "2017-06-26T18:41:00.9584103Z","id": "aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e","data": {"api": "PutBlockList","clientRequestId": "bbbb1b1b-cc2c-dd3d-ee4e-ffffff5f5f5f","requestId": "cccc2c2c-dd3d-ee4e-ff5f-aaaaaa6a6a6a","eTag": "0x8D4BCC2E4835CD0","contentType": "text/plain","contentLength": 524288,"blobType": "BlockBlob","url": "https://example.blob.core.windows.net/testcontainer/testfile.txt","sequencer": "00000000000004420000000000028963","storageDiagnostics": {"batchId": "dddd3d3d-ee4e-ff5f-aa6a-bbbbbb7b7b7b"}},"dataVersion": "","metadataVersion": "1"}]
2022-11-14T22:40:46.335 [Information] Got BlobCreated event data, blob URI https://example.blob.core.windows.net/testcontainer/testfile.txt
2022-11-14T22:40:46.346 [Information] Executed 'Function1' (Succeeded, Id=8429137d-9245-438c-8206-f9e85ef5dd61, Duration=387ms)
Вы также можете протестировать, создав учетную запись хранения BLOB-объектов или учетную запись хранения общего назначения версии 2, добавив подписку на событие и установив конечную точку в URL-адресе функции:
Обработка пользовательских событий
Наконец, давайте расширим функцию еще раз, чтобы она могла обрабатывать и пользовательские события.
Установите флажок для события Contoso.Items.ItemReceived. Итоговый код должен выглядеть следующим образом:
c#
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using Azure.Messaging.EventGrid;
using Azure.Messaging.EventGrid.SystemEvents;
namespaceFunction1
{
publicstaticclassFunction1
{
[FunctionName("Function1")]
publicstaticasync Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string response = string.Empty;
BinaryData events = await BinaryData.FromStreamAsync(req.Body);
log.LogInformation($"Received events: {events}");
EventGridEvent[] eventGridEvents = EventGridEvent.ParseMany(events);
foreach (EventGridEvent eventGridEvent in eventGridEvents)
{
// Handle system eventsif (eventGridEvent.TryGetSystemEventData(outobject eventData))
{
// Handle the subscription validation eventif (eventData is SubscriptionValidationEventData subscriptionValidationEventData)
{
log.LogInformation($"Got SubscriptionValidation event data, validation code: {subscriptionValidationEventData.ValidationCode}, topic: {eventGridEvent.Topic}");
// Do any additional validation (as required) and then return back the below responsevar responseData = new
{
ValidationResponse = subscriptionValidationEventData.ValidationCode
};
returnnew OkObjectResult(responseData);
}
// Handle the storage blob created eventelseif (eventData is StorageBlobCreatedEventData storageBlobCreatedEventData)
{
log.LogInformation($"Got BlobCreated event data, blob URI {storageBlobCreatedEventData.Url}");
}
}
// Handle the custom contoso eventelseif (eventGridEvent.EventType == "Contoso.Items.ItemReceived")
{
var contosoEventData = eventGridEvent.Data.ToObjectFromJson<ContosoItemReceivedEventData>();
log.LogInformation($"Got ContosoItemReceived event data, item SKU {contosoEventData.ItemSku}");
}
}
returnnew OkObjectResult(response);
}
}
}
JavaScript
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function begun');
var validationEventType = "Microsoft.EventGrid.SubscriptionValidationEvent";
var storageBlobCreatedEvent = "Microsoft.Storage.BlobCreated";
var customEventType = "Contoso.Items.ItemReceived";
for (var events in req.body) {
var body = req.body[events];
// Deserialize the event data into the appropriate type based on event typeif (body.data && body.eventType == validationEventType) {
context.log("Got SubscriptionValidation event data, validation code: " + body.data.validationCode + " topic: " + body.topic);
// Do any additional validation (as required) and then return back the below responsevar code = body.data.validationCode;
context.res = { status: 200, body: { "ValidationResponse": code } };
}
elseif (body.data && body.eventType == storageBlobCreatedEvent) {
var blobCreatedEventData = body.data;
context.log("Relaying received blob created event payload:" + JSON.stringify(blobCreatedEventData));
}
elseif (body.data && body.eventType == customEventType) {
var payload = body.data;
context.log("Relaying received custom payload:" + JSON.stringify(payload));
}
}
context.done();
};
Тестирование обработки пользовательских событий
Наконец, проверьте, может ли функция теперь обрабатывать ваш тип пользовательского события:
Вы также можете протестировать эту функциональность в реальном времени, отправив настраиваемое событие с помощью CURL на портале или отправив в пользовательский раздел с помощью любой службы или приложения, которые могут отправлять post в конечную точку. Создайте пользовательский раздел и подписку событий с помощью конечной точки, заданной в качестве URL-адреса функции.
Заголовки сообщений
Ниже приведены свойства, которые предоставляются в заголовках сообщений.
Имя свойства
Description
aeg-subscription-name
Имя подписки на события.
aeg-delivery-count
Число попыток, выполненных для события.
aeg-event-type
Тип события.
Может иметь одно из следующих значений.
SubscriptionValidation
Notification
SubscriptionDeletion
aeg-metadata-version
Версия метаданных события.
Для схемы событий Сетки событий это свойство представляет версию метаданных, а для схемы событий облака — версию спецификации.
aeg-data-version
Версия данных события.
Для схемы событий Сетки событий это свойство представляет версию данных, а для схемы событий облака оно не используется.
Присоединитесь к серии встреч для создания масштабируемых решений искусственного интеллекта на основе реальных вариантов использования с другими разработчиками и экспертами.