HTTP エンドポイントへのイベントの受信Receive events to an HTTP endpoint

この記事では、イベント サブスクリプションからイベントを受信する HTTP エンドポイントを検証した後、イベントを逆シリアル化する方法を説明します。This article describes how to validate an HTTP endpoint to receive events from an Event Subscription and then receive and deserialize events. この記事では、デモンストレーション用に Azure 関数を使用しますが、アプリケーションがどこでホストされている場合でも、同じ概念を適用できます。This article uses an Azure Function for demonstration purposes, however the same concepts apply regardless of where the application is hosted.

注意

Event Grid で Azure 関数をトリガーするときは、Event Grid トリガーを使用することを 強く お勧めします。It is strongly recommended that you use an Event Grid Trigger when triggering an Azure Function with Event Grid. ここでの汎用 WebHook トリガーの使用はデモンストレーションが目的です。The use of a generic WebHook trigger here is demonstrative.

前提条件Prerequisites

HTTP によってトリガーされる関数を含む関数アプリが必要です。You need a function app with an HTTP triggered function.

依存関係を追加するAdd dependencies

.NET で開発する場合は、Microsoft.Azure.EventGrid NuGet パッケージの関数に依存関係を追加しますIf you're developing in .NET, add a dependency to your function for the Microsoft.Azure.EventGrid NuGet package. この記事の例では、バージョン 1.4.0 以降が必要です。The examples in this article require version 1.4.0 or later.

他の言語用の SDK は、発行 SDK リファレンスを介して利用できます。SDKs for other languages are available via the Publish SDKs reference. これらのパッケージには、EventGridEvent``StorageBlobCreatedEventDataEventHubCaptureFileCreatedEventData などのネイティブなイベントの種類用のモデルが含まれています。These packages have the models for native event types such as EventGridEvent, StorageBlobCreatedEventData, and EventHubCaptureFileCreatedEventData.

Azure 関数 (Azure functions ポータルの右端のウィンドウ) の [ファイルの表示] リンクをクリックし、project.json という名前のファイルを作成します。Click on the "View Files" link in your Azure Function (right most pane in the Azure functions portal), and create a file called project.json. project.json ファイルに次の内容を追加し、保存します。Add the following contents to the project.json file and save it:

{
 "frameworks": {
   "net46":{
     "dependencies": {
       "Microsoft.Azure.EventGrid": "2.0.0"
     }
   }
  }
}

追加された NuGet パッケージ

エンドポイントの検証Endpoint validation

最初に実行することは、Microsoft.EventGrid.SubscriptionValidationEvent イベントの処理です。The first thing you want to do is handle Microsoft.EventGrid.SubscriptionValidationEvent events. だれかがイベントにサブスクライブするたびに、Event Grid は、データ ペイロード内に validationCode を含む検証イベントをエンドポイントに送信します。Every time someone subscribes to an event, Event Grid sends a validation event to the endpoint with a validationCode in the data payload. エンドポイントは、これを応答本文にエコー バックして、エンドポイントが有効であり、ユーザーによって所有されていることを証明する必要があります。The endpoint is required to echo this back in the response body to prove the endpoint is valid and owned by you. WebHook によってトリガーされる関数ではなく Event Grid トリガーを使用している場合、エンドポイントの検証は自動的に処理されます。If you're using an Event Grid Trigger rather than a WebHook triggered Function, endpoint validation is handled for you. サード パーティ製 API サービス (ZapierIFTTT など) を使用する場合は、検証コードをプログラムでエコーできないことがあります。If you use a third-party API service (like Zapier or IFTTT), you might not be able to programmatically echo the validation code. このようなサービスの場合は、サブスクリプション検証イベントで送信される検証 URL を使用すると、サブスクリプションを手動で検証できます。For those services, you can manually validate the subscription by using a validation URL that is sent in the subscription validation event. その URL を validationUrl プロパティにコピーし、REST クライアントまたは Web ブラウザーのいずれかを使って GET 要求を送信します。Copy that URL in the validationUrl property and send a GET request either through a REST client or your web browser.

C# では、DeserializeEventGridEvents()関数が Event Grid のイベントを逆シリアル化します。In C#, the DeserializeEventGridEvents() function deserializes the Event Grid events. これは、イベント データを StorageBlobCreatedEventData など、適切な型に逆シリアル化します。It deserializes the event data into the appropriate type, such as StorageBlobCreatedEventData. Microsoft.Azure.EventGrid.EventTypesクラスを使用してサポートされているイベントの種類と名前を取得します。Use the Microsoft.Azure.EventGrid.EventTypes class to get supported event types and names.

検証コードをプログラムでエコーするには、次のコードを使用します。To programmatically echo the validation code, use the following code. 関連するサンプルを Event Grid コンシューマー例で確認できます。You can find related samples at Event Grid Consumer example.

using System.IO;
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 Microsoft.Azure.EventGrid.Models;
using Microsoft.Azure.EventGrid;
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;
            string requestContent = await new StreamReader(req.Body).ReadToEndAsync();
            log.LogInformation($"Received events: {requestContent}");

            EventGridSubscriber eventGridSubscriber = new EventGridSubscriber();

            EventGridEvent[] eventGridEvents = eventGridSubscriber.DeserializeEventGridEvents(requestContent);

            foreach (EventGridEvent eventGridEvent in eventGridEvents)
            {
                if (eventGridEvent.Data is SubscriptionValidationEventData)
                {
                    var eventData = (SubscriptionValidationEventData)eventGridEvent.Data;
                    log.LogInformation($"Got SubscriptionValidation event data, validation code: {eventData.ValidationCode}, topic: {eventGridEvent.Topic}");
                    // Do any additional validation (as required) and then return back the below response

                    var responseData = new SubscriptionValidationResponse()
                    {
                        ValidationResponse = eventData.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();
};

検証の応答をテストするTest validation response

サンプル イベントを関数のテスト フィールドに貼り付けることによって、検証応答関数をテストします。Test the validation response function by pasting the sample event into the test field for the function:

[{
  "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"
}]

[実行] をクリックすると、200 OK と、本文に {"validationResponse":"512d38b6-c7b8-40c8-89fe-f46f9e9622b6"} が出力されます。When you click Run, the Output should be 200 OK and {"validationResponse":"512d38b6-c7b8-40c8-89fe-f46f9e9622b6"} in the body:

検証要求

検証の出力

BLOB ストレージ イベントを処理するHandle Blob storage events

次に、Microsoft.Storage.BlobCreated を処理するように関数を拡張してみましょう。Now, let's extend the function to handle Microsoft.Storage.BlobCreated:

using System.IO;
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 Microsoft.Azure.EventGrid;
using Microsoft.Azure.EventGrid.Models;

namespace FunctionApp1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation($"C# HTTP trigger function begun");
            string response = string.Empty;

            string requestContent = await new StreamReader(req.Body).ReadToEndAsync();
            log.LogInformation($"Received events: {requestContent}");

            EventGridSubscriber eventGridSubscriber = new EventGridSubscriber();

            EventGridEvent[] eventGridEvents = eventGridSubscriber.DeserializeEventGridEvents(requestContent);

            foreach (EventGridEvent eventGridEvent in eventGridEvents)
            {
                if (eventGridEvent.Data is SubscriptionValidationEventData)
                {
                    var eventData = (SubscriptionValidationEventData)eventGridEvent.Data;
                    log.LogInformation($"Got SubscriptionValidation event data, validation code: {eventData.ValidationCode}, topic: {eventGridEvent.Topic}");
                    // Do any additional validation (as required) and then return back the below response

                    var responseData = new SubscriptionValidationResponse()
                    {
                        ValidationResponse = eventData.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";
    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();
};

イベント処理用に作成した BLOB をテストするTest Blob Created event handling

テスト フィールドに BLOB ストレージ イベントを配置して実行することで、関数の新しい機能をテストします。Test the new functionality of the function by putting a Blob storage event into the test field and running:

[{
  "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"
}]

関数ログに BLOB URL が出力されます。You should see the blob URL output in the function log:

出力ログ

Blob ストレージ アカウントまたは General Purpose V2 (GPv2) Storage アカウントを作成し、イベント サブスクリプションを追加し、エンドポイントを関数 URL に設定してテストすることもできます。You can also test by creating a Blob storage account or General Purpose V2 (GPv2) Storage account, adding and event subscription, and setting the endpoint to the function URL:

関数の URL

カスタム イベントを処理するHandle Custom events

最後に、関数をもう一度拡張して、カスタム イベントも処理できるようにしましょう。Finally, lets extend the function once more so that it can also handle custom events.

C# では、SDK は、イベントの種類名の、イベントのデータ型へのマッピングをサポートします。In C#, the SDK supports mapping an event type name to the event data type. AddOrUpdateCustomEventMapping()関数を使用してカスタム イベントをマップします。Use the AddOrUpdateCustomEventMapping() function to map the custom event.

イベント Contoso.Items.ItemReceived のチェックを追加します。Add a check for your event Contoso.Items.ItemReceived. 最終的なコードは、次のようになります。Your final code should look like:

using System.IO;
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 Microsoft.Azure.EventGrid.Models;
using Microsoft.Azure.EventGrid;
using Newtonsoft.Json;

namespace FunctionApp1
{
    class ContosoItemReceivedEventData
    {
        [JsonProperty("itemSku")]
        public string ItemSku { get; set; }
    }
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation($"C# HTTP trigger function begun");
            string response = string.Empty;

            string requestContent = await new StreamReader(req.Body).ReadToEndAsync();
            log.LogInformation($"Received events: {requestContent}");

            EventGridSubscriber eventGridSubscriber = new EventGridSubscriber();
            eventGridSubscriber.AddOrUpdateCustomEventMapping("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData));
            EventGridEvent[] eventGridEvents = eventGridSubscriber.DeserializeEventGridEvents(requestContent);

            foreach (EventGridEvent eventGridEvent in eventGridEvents)
            {
                if (eventGridEvent.Data is SubscriptionValidationEventData)
                {
                    var eventData = (SubscriptionValidationEventData)eventGridEvent.Data;
                    log.LogInformation($"Got SubscriptionValidation event data, validation code: {eventData.ValidationCode}, topic: {eventGridEvent.Topic}");
                    // Do any additional validation (as required) and then return back the below response

                    var responseData = new SubscriptionValidationResponse()
                    {
                        ValidationResponse = eventData.ValidationCode
                    };

                    return new OkObjectResult(responseData);
                }
                else if (eventGridEvent.Data is StorageBlobCreatedEventData)
                {
                    var eventData = (StorageBlobCreatedEventData)eventGridEvent.Data;
                    log.LogInformation($"Got BlobCreated event data, blob URI {eventData.Url}");
                }
                else if (eventGridEvent.Data is ContosoItemReceivedEventData)
                {
                    var eventData = (ContosoItemReceivedEventData)eventGridEvent.Data;
                    log.LogInformation($"Got ContosoItemReceived event data, item SKU {eventData.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();
};

カスタム イベントの処理をテストするTest custom event handling

最後に、関数がカスタム イベントの種類を処理できるようになっていることをテストします。Finally, test that your function can now handle your custom event type:

[{
    "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 などのエンドポイントに POST できる任意のサービスまたはアプリケーションを使用してカスタム トピックを投稿することで、ライブでテストすることもできます。You can also test this functionality live by sending a custom event with CURL from the Portal or by posting to a custom topic using any service or application that can POST to an endpoint such as Postman. エンドポイントが関数 URL として設定されたカスタム トピックとイベント サブスクリプションを作成します。Create a custom topic and an event subscription with the endpoint set as the Function URL.

メッセージ ヘッダーMessage headers

これらは、メッセージ ヘッダーで受け取るプロパティです。These are the properties you receive in the message headers:

プロパティ名Property name 説明Description
aeg-subscription-nameaeg-subscription-name イベント サブスクリプションの名前。Name of the event subscription.
aeg-delivery-countaeg-delivery-count イベントに対して行われた試行の回数。Number of attempts made for the event.
aeg-event-typeaeg-event-type

イベントの種類。Type of the event.

次のいずれかの値を指定できます。It can be one of the following values:

  • SubscriptionValidationSubscriptionValidation
  • NotificationNotification
  • SubscriptionDeletionSubscriptionDeletion
aeg-metadata-versionaeg-metadata-version

イベントのメタデータ バージョン。Metadata version of the event.

Event Grid のイベント スキーマ の場合、このプロパティはメタデータのバージョンを表し、クラウド イベント スキーマ の場合は 仕様のバージョン を表します。For Event Grid event schema, this property represents the metadata version and for cloud event schema, it represents the spec version.

aeg-data-versionaeg-data-version

イベントのデータ バージョン。Data version of the event.

Event Grid のイベント スキーマ の場合、このプロパティはデータのバージョンを表し、クラウド イベント スキーマ の場合は適用されません。For Event Grid event schema, this property represents the data version and for cloud event schema, it doesn't apply.

aeg-output-event-idaeg-output-event-id Event Grid イベントの ID。ID of the Event Grid event.

次のステップNext steps