تلقي الأحداث إلى نقطة نهاية HTTP

توضح هذه المقالة كيفية التحقق من صحة نقطة نهاية HTTP لتلقي الأحداث من اشتراك حدث وتلقي الأحداث وإلغاء تسلسلها. تستخدم هذه المقالة دالة Azure لأغراض العرض التوضيحي، ولكن نفس المفاهيم تنطبق بغض النظر عن مكان استضافة التطبيق.

إشعار

نوصي باستخدام مشغل شبكة الأحداث عند تشغيل وظيفة Azure مع Event Grid. يوفر تكاملاً أسهل وأسرع بين Event Grid ودوال Azure. ومع ذلك، لاحظ أن مشغل Event Grid ل Azure Functions لا يدعم السيناريو حيث تحتاج التعليمات البرمجية المستضافة إلى التحكم في رمز حالة HTTP الذي تم إرجاعه إلى Event Grid. نظراً لهذا القيد، فعلى سبيل المثال لن تتمكن التعليمات البرمجية التي تعمل على دالة Azure من إرجاع خطأ 5XX لبدء إعادة محاولة تسليم حدث بواسطة Event Grid.

المتطلبات الأساسية

تحتاج إلى تطبيق دالة مع دالة يشغلها HTTP.

إضافة تبعيات

إذا كنت تقوم بتطوير في .NET، أضف تبعية إلى الدالة Azure.Messaging.EventGridلحزمة NuGet.

تتوفر SDKs للغات الأخرى عبر مرجع نشر SDKs. تحتوي هذه الحزم على نماذج لأنواع الأحداث الأصلية مثل EventGridEvent، وStorageBlobCreatedEventData، وEventHubCaptureFileCreatedEventData.

التحقق من صحة نقطة النهاية

أول شيء تريد القيام به هو معالجة أحداث Microsoft.EventGrid.SubscriptionValidationEvent. في كل مرة يشترك فيها شخص ما في حدث ما، ترسل Event Grid حدث تحقق من الصحة إلى نقطة النهاية مع validationCode في حمولة البيانات. نقطة النهاية مطلوبة لتكرار هذا في نص الاستجابة لإثبات أن نقطة النهاية صالحة ومملوكة لك. إذا كنت تستخدم مشغل Event Grid بدلاً من دالة يشغلها WebHook، فسيتم التعامل مع التحقق من صحة نقطة النهاية نيابة عنك. إذا كنت تستخدم خدمة واجهة برمجة التطبيقات لجهة خارجية (مثل Zapier أو IFTTT)، فقد لا تتمكن من تكرار رمز التحقق برمجياً. بالنسبة لهذه الخدمات، يمكنك التحقق من صحة الاشتراك يدوياً باستخدام عنوان URL للتحقق من الصحة الذي يتم إرساله في حدث التحقق من صحة الاشتراك. انسخ عنوان URL هذا في الخاصية validationUrl وأرسل طلب GET إما من خلال عميل REST أو متصفح الويب الخاص بك.

في C#، ParseMany() يتم استخدام الأسلوب لإلغاء تسلسل BinaryData مثيل يحتوي على حدث واحد أو أكثر في صفيف من EventGridEvent. إذا كنت تعرف مسبقا أنك تقوم بإلغاء تسلسل حدث واحد فقط، يمكنك استخدام Parse الأسلوب بدلا من ذلك.

لتكرار رمز التحقق من الصحة برمجياً، استخدم التعليمات البرمجية التالية.

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;

namespace Function1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async 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 events
                if (eventGridEvent.TryGetSystemEventData(out object eventData))
                {
                    // Handle the subscription validation event
                    if (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 response
                        var responseData = new
                        {
                            ValidationResponse = subscriptionValidationEventData.ValidationCode
                        };

                        return new OkObjectResult(responseData);
                    }
                }
            }
            return new OkObjectResult(response);
        }
    }
}
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 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 response
            var code = body.data.validationCode;
            context.res = { status: 200, body: { "ValidationResponse": code } };
        }
    }
    context.done();
};

اختبار استجابة التحقق من الصحة

اختبار دالة استجابة التحقق من الصحة عن طريق لصق الحدث النموذج في حقل الاختبار للدالة:

[{
  "id": "2d1781af-3a4c-4d7c-bd0c-e34b19da4e66",
  "topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "subject": "",
  "data": {
    "validationCode": "512d38b6-c7b8-40c8-89fe-f46f9e9622b6"
  },
  "eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
  "eventTime": "2018-01-25T22:12:19.4556811Z",
  "metadataVersion": "1",
  "dataVersion": "1"
}]

عند تحديد Run، يجب أن يكون الإخراج 200 OK وفي {"validationResponse":"512d38b6-c7b8-40c8-89fe-f46f9e9622b6"} النص الأساسي:

Validation request

Validation output

معالجة أحداث مخزن البيانات الثنائية الكبيرة

الآن، دعنا نوسع الدالة لمعالجة حدث النظام Microsoft.Storage.BlobCreated:

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;

namespace Function1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async 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 events
                if (eventGridEvent.TryGetSystemEventData(out object eventData))
                {
                    // Handle the subscription validation event
                    if (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 response

                        var responseData = new
                        {
                            ValidationResponse = subscriptionValidationEventData.ValidationCode
                        };
                        return new OkObjectResult(responseData);
                    }
                    // Handle the storage blob created event
                    else if (eventData is StorageBlobCreatedEventData storageBlobCreatedEventData)
                    {
                        log.LogInformation($"Got BlobCreated event data, blob URI {storageBlobCreatedEventData.Url}");
                    }
                }
            }
            return new OkObjectResult(response);
        }
    }
}
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 response
            var code = body.data.validationCode;
            context.res = { status: 200, body: { "ValidationResponse": code } };
        }

        else if (body.data && body.eventType == storageBlobCreatedEvent) {
            var blobCreatedEventData = body.data;
            context.log("Relaying received blob created event payload:" + JSON.stringify(blobCreatedEventData));
        }
    }
    context.done();
};

اختبار معالجة حدث أنشأها كائن ثنائي كبير الحجم

اختبار الوظيفة الجديدة للدالة عن طريق وضع حدث تخزين كائن ثنائي كبير الحجم في حقل الاختبار وتشغيله:

[{
  "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": "831e1650-001e-001b-66ab-eeb76e069631",
  "data": {
    "api": "PutBlockList",
    "clientRequestId": "6d79dbfb-0e37-4fc4-981f-442c9ca65760",
    "requestId": "831e1650-001e-001b-66ab-eeb76e000000",
    "eTag": "0x8D4BCC2E4835CD0",
    "contentType": "text/plain",
    "contentLength": 524288,
    "blobType": "BlockBlob",
    "url": "https://example.blob.core.windows.net/testcontainer/testfile.txt",
    "sequencer": "00000000000004420000000000028963",
    "storageDiagnostics": {
      "batchId": "b68529f3-68cd-4744-baa4-3c0498ec19f0"
    }
  },
  "dataVersion": "",
  "metadataVersion": "1"
}]

يجب أن تشاهد إخراج عنوان URL للكائن الثنائي كبير الحجم في سجل الدالة:

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": "831e1650-001e-001b-66ab-eeb76e069631","data": {"api": "PutBlockList","clientRequestId": "6d79dbfb-0e37-4fc4-981f-442c9ca65760","requestId": "831e1650-001e-001b-66ab-eeb76e000000","eTag": "0x8D4BCC2E4835CD0","contentType": "text/plain","contentLength": 524288,"blobType": "BlockBlob","url": "https://example.blob.core.windows.net/testcontainer/testfile.txt","sequencer": "00000000000004420000000000028963","storageDiagnostics": {"batchId": "b68529f3-68cd-4744-baa4-3c0498ec19f0"}},"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 أو حساب تخزين للأغراض العامة V2، وإضافة اشتراك حدث، وتعيين نقطة النهاية إلى عنوان URL للوظيفة:

Function URL

معالجة الأحداث المخصصة

وأخيراً، دعنا نوسع الدالة مرة أخرى بحيث يمكنها أيضاً معالجة الأحداث المخصصة.

إضافة فحص للحدث Contoso.Items.ItemReceived الخاص بك. يجب أن تبدو التعليمة البرمجية النهائية كما يلي:

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;

namespace Function1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async 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 events
                if (eventGridEvent.TryGetSystemEventData(out object eventData))
                {
                    // Handle the subscription validation event
                    if (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 response

                        var responseData = new
                        {
                            ValidationResponse = subscriptionValidationEventData.ValidationCode
                        };
                        return new OkObjectResult(responseData);
                    }
                    // Handle the storage blob created event
                    else if (eventData is StorageBlobCreatedEventData storageBlobCreatedEventData)
                    {
                        log.LogInformation($"Got BlobCreated event data, blob URI {storageBlobCreatedEventData.Url}");
                    }
                }
                // Handle the custom contoso event
                else if (eventGridEvent.EventType == "Contoso.Items.ItemReceived")
                {
                    var contosoEventData = eventGridEvent.Data.ToObjectFromJson<ContosoItemReceivedEventData>();
                    log.LogInformation($"Got ContosoItemReceived event data, item SKU {contosoEventData.ItemSku}");
                }
            }
            return new OkObjectResult(response);
        }
    }
}
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 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 response
            var code = body.data.validationCode;
            context.res = { status: 200, body: { "ValidationResponse": code } };
        }

        else if (body.data && body.eventType == storageBlobCreatedEvent) {
            var blobCreatedEventData = body.data;
            context.log("Relaying received blob created event payload:" + JSON.stringify(blobCreatedEventData));
        }

        else if (body.data && body.eventType == customEventType) {
            var payload = body.data;
            context.log("Relaying received custom payload:" + JSON.stringify(payload));
        }
    }
    context.done();
};

اختبار معالجة الحدث المخصص

وأخيراً، اختبر أن الدالة يمكنها الآن معالجة نوع الحدث المخصص:

[{
    "subject": "Contoso/foo/bar/items",
    "eventType": "Contoso.Items.ItemReceived",
    "eventTime": "2017-08-16T01:57:26.005121Z",
    "id": "602a88ef-0001-00e6-1233-1646070610ea",
    "data": { 
            "itemSku": "Standard"
            },
    "dataVersion": "",
    "metadataVersion": "1"
}]

يمكنك أيضاً اختبار هذه الوظيفة مباشرة عن طريق إرسال حدث مخصص مع CURL من المدخل أو عن طريق النشر إلى موضوع مخصص باستخدام أي خدمة أو تطبيق يمكنه النشر إلى نقطة نهاية مثل Postman. إنشاء موضوع مخصص واشتراك حدث مع تعيين نقطة النهاية على أنها عنوان URL للدالة.

عناوين الرسائل

هذه هي الخصائص التي تتلقاها في عناوين الرسائل:

اسم الخاصية ‏‏الوصف
اسم اشتراك aeg اسم اشتراك الحدث.
aeg التسليم -العد عدد المحاولات التي تمت للحدث.
aeg-الحدث نوع

نوع الحدث.

يمكن أن تكون إحدى القيم التالية:

  • SubscriptionValidation
  • الإعلام
  • SubscriptionDeletion
aeg-metadata-version

إصدار بيانات التعريف للحدث.

بالنسبة إلى مخطط حدث شبكة الأحداث، تمثل هذه الخاصية إصدار بيانات التعريف ومخطط حدث مجموعة النظراء، وهي تمثل إصدار المواصفات.

aeg- إصدار البيانات

إصدار بيانات الحدث.

بالنسبة إلى مخطط حدث شبكة الأحداث، تمثل هذه الخاصية إصدار البيانات ومخطط حدث مجموعة النظراء، ولا يتم تطبيقها.

aeg-إخراج-الحدث-معرف معرف حدث شبكة الأحداث.

الخطوات التالية