Loupe

Implémenter un Webhook générique pour Event Grid

Rapide introduction sur Event Grid

Azure Event Grid est un système de routage / distribution de messages (événements) supportant la haute disponibilité et capable d'encaisser un volume massif de messages.

Le principe est simple. D'un côté des services publient des messages pour des "topics" particuliers que l'on peut définir comme une catégorie de message, certains étant systèmes car gérés par Azure, d'autres personnalisés puisque vous pouvez définir vos propres événements. De l'autre des services s'abonnent à ces topics pour les consommer et réagir à leur réception. 

On peut ainsi mettre en place une gestion événementielle facilement et à un coût réduit (les premières 100 000 opérations/mois sont gratuites, les 100k suivantes sont à 0,30€, le détail du pricing ici).

Ci-dessous la plupart des services supportés à ce jour, il ne  manque qu'Azure Queue Storage et Azure Relay Hybrid Connection annoncés lors de la Build 2018 (plus d'infos sur Event Grid) :EventGridOverview.png

 Toujours est-il qu'il est à ce jour très simple de développer son Azure Function pour réagir à ces événements, mais qu'il n'existe pas/peu de documentation pour réaliser son propre WebHook, par exemple si vous désirez l'intégrer directement au sein d'un site existant. Voici donc la marche à suivre (l'exemple donné utilisé une application en .NET classique mais l'approche est en tout point similaire en .NET Core).

Fonctionnement général

Lors de l'abonnement à un sujet particulier, Azure va d'abord envoyer un code de validation qui devra être renvoyé par le WebHook. Cela permet de s'assurer que celui-ci est bien prêt à répondre aux futures requêtes (plus de détail sur le schéma de l'abonnement)

Une fois l'abonnement réalisé, lors de la réception d'événements, Azure Event Grid appelera les différents endpoints abonnés au sujet concerné et respectant les éventuels filtres. A noter que vous pourrez recevoir plusieurs événements dans un seul appel car Azure gèrera une sorte de buffer pour éviter de trop spammer votre service!

Place à l'implémentation

L'exemple suivant est tiré de la documentation pour créer un endpoint générique, en l'adaptant à une web app plutôt qu'à une Azure Function.

Commencez donc par ajouter le package "Microsoft.Azure.EventGrid" afin de bénéficier des classes et donc utiliser des objets fortement typés plutôt que de devoir parser le JSON envoyé.
EventGridWebHook03.PNG

Ensuite, il suffit d'implémenter une méthode en POST avec le code suivant:

[HttpPost]
public HttpResponseMessage Post([FromBody] EventGridEvent[] eventGridEvents, string secret = null)
{
    if (secret != "monpetitsecret")
    {
        return new HttpResponseMessage(HttpStatusCode.BadRequest)
        {
            Content = new StringContent(JsonConvert.SerializeObject("Wrong secret"), Encoding.UTF8, "application/json")
        };
    }
    // Subscription Event Type
    const string SubscriptionValidationEvent = "Microsoft.EventGrid.SubscriptionValidationEvent";
            
    foreach (EventGridEvent eventGridEvent in eventGridEvents)
    {
        JObject dataObject = eventGridEvent.Data as JObject;

        // Check if it's a subscription validation
        if (string.Equals(eventGridEvent.EventType, SubscriptionValidationEvent, StringComparison.OrdinalIgnoreCase))
        {
            var eventData = dataObject.ToObject<SubscriptionValidationEventData>();
            Trace.TraceInformation($"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();
            responseData.ValidationResponse = eventData.ValidationCode;

            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(JsonConvert.SerializeObject(responseData), Encoding.UTF8, "application/json")
            };
        }
        else
        {
            // Manage events
            Trace.TraceInformation($"Event Type : {eventGridEvent.EventType}, event time : {eventGridEvent.EventTime}, data : {eventGridEvent.Data.ToString()}");
        }
    }

    return new HttpResponseMessage(HttpStatusCode.OK);
}

 

J'ai intégré un paramètre "secret" envoyé en querystring, pouvant être utilisé lors de l'abonnement du WebHook et qui sera envoyé à chaque appel, afin de sécuriser un peu plus celui-ci comme expliqué dans la documentation. Ca peut être un access token ou n'importe quelle chaîne de caractères que vous aurez choisi (et qui sera surtout plus complexe que "monpetitsecret" j'espère!). Je renvoie actuellement un message indiquant que le secret n'est pas bon, mais il est conseillé de ne renvoyer qu'un code d'erreur (BadRequest ou autre) afin de ne pas indiquer à un éventuel petit malin par où chercher.

Le reste du code est très simple : on parcourt la liste d'événements reçus :

  • si c'est une demande de validation, on renvoie le message adéquat avec le code fourni
  • sinon vous traitez l'événement (ici je trace l'événement)

Il n'y a plus qu'à tester votre WebHook avant de le déclarer dans Event Grid à l'aide de Postman par exemple.

Test de validation (le code de validation renvoyé doit être le même que celui fourni en entrée):
Event Grid - Test - Validation

Et test d'envoi de plusieurs événements :
Event Grid - Test - Events.PNG

Et bien sûr en cas de mauvais secret :
Event Grid - Test - Bad Secret

A vous de jouer maintenant !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus