Utiliser des webhooks pour créer des gestionnaires externes pour les événements de serveur

Avec Microsoft Dataverse, vous pouvez envoyer des données sur les événements qui se produisent sur le serveur à une application Web à l’aide de webhooks. Une webhook est un protocole HTTP léger permettant de connecter des API web et des services avec un modèle de publication/abonnement. Les expéditeurs de webhooks avertissent les destinataires à propos d’événements en adressant aux points de terminaison du destinataire des demandes comportant certaines informations sur les événements.

Les webhooks permettent aux développeurs et aux éditeurs de logiciels indépendants d’intégrer des données de Dataverse dans leur propre code personnalisé hébergé sur des services externes. Grâce au modèle webhooks, vous pouvez sécuriser votre point de terminaison à l’aide d’un en-tête d’authentification ou de clés de paramètre de chaîne de requête. Cette méthode est plus simple que le modèle d’authentification SAS que vous utilisez peut-être actuellement pour l’intégration de Azure Service Bus.

Au moment de faire votre choix entre le modèle webhook et l’intégration de Azure Service Bus, voici quelques éléments pour fonder votre réflexion :

  • Azure Service Bus fonctionne pour le traitement à grande échelle et offre un mécanisme complet de mise en file si Dataverse envoie beaucoup d’événements.
  • Les webhooks ne peuvent évoluer que jusqu’au point auquel votre service Web hébergé peut gérer les messages.
  • Les webhooks permettent des étapes synchrones et asynchrones. Azure Service Bus autorise uniquement les étapes asynchrones.
  • Les webhooks envoient des requêtes POST avec une charge utile JSON et sont consommables par n’importe quel langage de programmation ou application Web hébergée n’importe où.
  • Il est possible d’appeler aussi bien les webhooks que Azure Service Bus à partir d’une activité de workflow personnalisée ou d’un plug-in.

Démarrer

L’utilisation des webhooks comprend trois parties :

  • la création ou la configuration d’un service pour traiter les requêtes de webhook ;
  • une étape d’inscription du webhook sur le service Dataverse, ou
  • l’appel d’un webhook à partir d’un plug-in ou d’une activité de workflow personnalisée.

Commencez par inscrire un webhook de test

Pour comprendre comment créer et configurer un service pour utiliser une requête de webhook à partir de Dataverse, il est utile de commencer par comprendre comment inscrire un webhook. Pour plus d’informations, voir : Inscrire un webhook

Lorsque vous avez inscrit un exemple de webhook, vous pouvez utiliser un site de journalisation des requêtes pour examiner les données contextuelles qui seront transmises. Pour plus d’informations, voir : Tester l’inscription du webhook avec un site de journalisation des requêtes

Conseil

Effectuer les étapes d’inscription d’un webhook de test et examiner les données contextuelles transmises aideront à mieux comprendre le reste des informations de cette rubrique. Effectuez les étapes suivantes et revenez à cette rubrique.

Créer ou configurer un service pour traiter les requêtes de webhook

Les webhooks sont simplement un modèle utilisable dans un large éventail de technologies. Ils ne requièrent aucun framework, plateforme ou langage de programmation particulier. Utilisez vos compétences et vos connaissances pour fournir la solution appropriée.

Les Fonctions Azure fournissent un excellent moyen de développement d’une solution à l’aide de webhooks, mais ce n’est pas obligatoire. Cette section ne fournit pas d’instructions par rapport à une solution spécifique, mais décrit plutôt les données qui seront transmises à votre service et lui permettront d’être utile.

Comme montré dans la section Tester l’inscription du webhook avec un site de journalisation des requêtes, vous pouvez inscrire une étape de webhook de test et utiliser le site de journalisation des requêtes pour recueillir les types de données spécifiques que votre application peut traiter.

Données transmises au service

Il existe trois types de données dans la requête : chaîne de requête, données d’en-tête et corps de requête.

Chaîne de requête

Le seul type de données qui sont transmises sous forme de chaîne de requête sont les valeurs d’authentification, si le webhook est configuré pour utiliser les options WebhookKey ou HttpQueryString, décrites dans Options d’authentification.

Données d’en-tête

Si vous choisissez l’option d’authentification HttpHeader, vous devrez utiliser les paire clé/valeur demandées par votre service.

Les autres données que vous pourrez voir transmises à votre service se trouvent dans le tableau ci-dessous :

Clé Description de la valeur
x-ms-dynamics-organization Nom du domaine de l’environnement qui envoie la requête
x-ms-dynamics-entity-name Nom logique de la table passée dans les données du contexte d’exécution.
x-ms-dynamics-request-name Nom de l’événement pour lequel l’étape de webhook a été enregistrée.
x-ms-correlation-request-id Identificateur unique du suivi de tout type d’extension. Cette propriété est utilisée par la plateforme pour empêcher les boucles infinies. Dans la plupart des cas, cette propriété peut être ignorée. Cette valeur peut être utilisée pour travailler avec le support technique car elle peut servir à interroger la télémétrie afin de savoir ce qui s’est passé pendant toute l’opération.
x-ms-dynamics-msg-size-exceeded Ne l’envoyez que quand la charge utile HTTP dépasse les 256 Ko.

Corps de la requête

Le corps contient une chaîne représentant la valeur JSON d’une instance de la classe RemoteExecutionContext. Ce sont les mêmes données qui sont transférées aux intégrations du Service Bus Azure.

Le service que vous avez créé doit analyser ces données pour extraire les éléments d’informations appropriés pour que votre service assure sa fonction. Votre choix d’analyser ces données dépend de la technologie que vous utilisez et de vos préférences.

Ce qui suit est un exemple de données JSON sérialisées passées pour une étape inscrite avec les propriétés suivantes :

Propriété Description
Message Mettre à jour
Entité principale contact
Entité secondaire aucun(e)
Filtrage d’attributs prénom, nom
Exécuter dans le contexte utilisateur Utilisateur appelant
Ordre d’exécution 1
Phase d’exécution dans le pipeline d’événement PostOperation
Mode d’exécution Asynchrone
{
    "BusinessUnitId": "e2b9dd85-e89e-e711-8122-000d3aa2331c",
    "CorrelationId": "b374239d-4233-41a9-8b17-a86cb4f737b5",
    "Depth": 1,
    "InitiatingUserId": "75c2dd85-e89e-e711-8122-000d3aa2331c",
    "InputParameters": [{
        "key": "Target",
        "value": {
            "__type": "Entity:http:\/\/schemas.microsoft.com\/xrm\/2011\/Contracts",
            "Attributes": [{
                "key": "firstname",
                "value": "James"
            }, {
                "key": "contactid",
                "value": "6d81597f-0f9f-e711-8122-000d3aa2331c"
            }, {
                "key": "fullname",
                "value": "James Glynn (sample)"
            }, {
                "key": "yomifullname",
                "value": "James Glynn (sample)"
            }, {
                "key": "modifiedon",
                "value": "\/Date(1506384247000)\/"
            }, {
                "key": "modifiedby",
                "value": {
                    "__type": "EntityReference:http:\/\/schemas.microsoft.com\/xrm\/2011\/Contracts",
                    "Id": "75c2dd85-e89e-e711-8122-000d3aa2331c",
                    "KeyAttributes": [],
                    "LogicalName": "systemuser",
                    "Name": null,
                    "RowVersion": null
                }
            }, {
                "key": "modifiedonbehalfby",
                "value": null
            }],
            "EntityState": null,
            "FormattedValues": [],
            "Id": "6d81597f-0f9f-e711-8122-000d3aa2331c",
            "KeyAttributes": [],
            "LogicalName": "contact",
            "RelatedEntities": [],
            "RowVersion": null
        }
    }],
    "IsExecutingOffline": false,
    "IsInTransaction": false,
    "IsOfflinePlayback": false,
    "IsolationMode": 1,
    "MessageName": "Update",
    "Mode": 1,
    "OperationCreatedOn": "\/Date(1506409448000-0700)\/",
    "OperationId": "4af10637-4ea2-e711-8122-000d3aa2331c",
    "OrganizationId": "4ef5b371-e89e-e711-8122-000d3aa2331c",
    "OrganizationName": "OrgName",
    "OutputParameters": [],
    "OwningExtension": {
        "Id": "75417616-4ea2-e711-8122-000d3aa2331c",
        "KeyAttributes": [],
        "LogicalName": "sdkmessageprocessingstep",
        "Name": null,
        "RowVersion": null
    },
    "ParentContext": {
        "BusinessUnitId": "e2b9dd85-e89e-e711-8122-000d3aa2331c",
        "CorrelationId": "b374239d-4233-41a9-8b17-a86cb4f737b5",
        "Depth": 1,
        "InitiatingUserId": "75c2dd85-e89e-e711-8122-000d3aa2331c",
        "InputParameters": [{
            "key": "Target",
            "value": {
                "__type": "Entity:http:\/\/schemas.microsoft.com\/xrm\/2011\/Contracts",
                "Attributes": [{
                    "key": "firstname",
                    "value": "James"
                }, {
                    "key": "contactid",
                    "value": "6d81597f-0f9f-e711-8122-000d3aa2331c"
                }],
                "EntityState": null,
                "FormattedValues": [],
                "Id": "6d81597f-0f9f-e711-8122-000d3aa2331c",
                "KeyAttributes": [],
                "LogicalName": "contact",
                "RelatedEntities": [],
                "RowVersion": null
            }
        }, {
            "key": "SuppressDuplicateDetection",
            "value": false
        }],
        "IsExecutingOffline": false,
        "IsInTransaction": false,
        "IsOfflinePlayback": false,
        "IsolationMode": 1,
        "MessageName": "Update",
        "Mode": 1,
        "OperationCreatedOn": "\/Date(1506409448000-0700)\/",
        "OperationId": "4af10637-4ea2-e711-8122-000d3aa2331c",
        "OrganizationId": "4ef5b371-e89e-e711-8122-000d3aa2331c",
        "OrganizationName": "OneFarm",
        "OutputParameters": [],
        "OwningExtension": {
            "Id": "75417616-4ea2-e711-8122-000d3aa2331c",
            "KeyAttributes": [],
            "LogicalName": "sdkmessageprocessingstep",
            "Name": null,
            "RowVersion": null
        },
        "ParentContext": null,
        "PostEntityImages": [],
        "PreEntityImages": [],
        "PrimaryEntityId": "6d81597f-0f9f-e711-8122-000d3aa2331c",
        "PrimaryEntityName": "contact",
        "RequestId": null,
        "SecondaryEntityName": "none",
        "SharedVariables": [{
            "key": "ChangedEntityTypes",
            "value": [{
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "feedback",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "contract",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "salesorder",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "connection",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "socialactivity",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "postfollow",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "incident",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "invoice",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "entitlement",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "lead",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "opportunity",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "quote",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "socialprofile",
                "value": "Update"
            }, {
                "__type": "KeyValuePairOfstringstring:#System.Collections.Generic",
                "key": "contact",
                "value": "Update"
            }]
        }],
        "Stage": 30,
        "UserId": "75c2dd85-e89e-e711-8122-000d3aa2331c"
    },
    "PostEntityImages": [{
        "key": "AsynchronousStepPrimaryName",
        "value": {
            "Attributes": [{
                "key": "fullname",
                "value": "James Glynn (sample)"
            }, {
                "key": "contactid",
                "value": "6d81597f-0f9f-e711-8122-000d3aa2331c"
            }],
            "EntityState": null,
            "FormattedValues": [],
            "Id": "6d81597f-0f9f-e711-8122-000d3aa2331c",
            "KeyAttributes": [],
            "LogicalName": "contact",
            "RelatedEntities": [],
            "RowVersion": null
        }
    }],
    "PreEntityImages": [],
    "PrimaryEntityId": "6d81597f-0f9f-e711-8122-000d3aa2331c",
    "PrimaryEntityName": "contact",
    "RequestId": null,
    "SecondaryEntityName": "none",
    "SharedVariables": [],
    "Stage": 40,
    "UserId": "75c2dd85-e89e-e711-8122-000d3aa2331c"
}

Important

Lorsque la taille de la charge utile HTTP dépasse 256 Ko, l’en-tête x-ms-dynamics-msg-size-exceeded est inclus et les propriétés RemoteExecutionContext suivantes sont supprimées :

Certaines opérations ne comprennent pas ces propriétés.

Appeler un webhook à partir d’un plug-in ou d’une activité de workflow

Comme un webhook est un type de point de terminaison de service, vous pouvez également l’appeler sans inscrire d’étape avec un plug-in ou une activité de workflow, de la même manière que vous pouvez le faire pour un point de terminaison Azure Service Bus. Vous devez indiquer l’ID ServiceEndpointId à l’interface IServiceEndpointNotificationService. Pour plus d’informations, voir les exemples Azure Service Bus suivants :

Résoudre les problèmes d’inscription du webhook

les webhooks sont assez simples. Le service envoie la requête et évalue la réponse. Le système ne peut pas analyser les données renvoyées avec le corps de la réponse, il se contente de regarder la valeur du StatusCode de la réponse.

Le délai d’attente est de 60 secondes. En général, si aucune réponse n’est renvoyée avant le délai d’expiration ou si la valeur StatusCode de la réponse n’est pas dans la plage 2xx qui indique la réussite, la requête échoue. Il y a une exception lorsque l’erreur renvoyée est dans le tableau suivant :

StatusCode Description
502 Passerelle incorrecte
503 Service indisponible
504 Délai d’expiration de passerelle

Ces erreurs indiquent un problème de réseau qui peut être résolu lors d’une autre tentative. Le service du webhook n’effectue de nouvelle tentative que si ces codes d’erreur sont renvoyés.

Webhooks asynchrones

Si votre webhook est inscrit pour s’exécuter en mode asynchrone, vous pouvez examiner la tâche système pour obtenir des détails sur l’erreur. Pour plus d’informations, voir : Tâches asynchrones de requête échouées pour une étape donnée

Webhooks synchrones

Lorsque vous choisissez d’utiliser un mode d’exécution synchrone, toute défaillance est signalée à l’utilisateur de l’application par une boîte de dialogue d’erreur Point de terminaison non disponible informant l’utilisateur que le point de terminaison du service de webhook peut être configuré incorrectement ou n’est pas disponible. Cette boîte de dialogue vous permet de télécharger un fichier journal pour avoir des détails sur les erreurs.

Notes

Tout webhook enregistré pour une étape synchrone envoie immédiatement les données du contexte d’exécution au point de terminaison configuré. Si une erreur se produit après l’envoi de la requête, l’opération de données est annulée mais la requête envoyée au point de terminaison configuré ne peut pas être rappelée.

Étapes suivantes

Inscrire un webhook
Tester l’inscription du webhook avec un site de journalisation des requêtes

Voir aussi

Écrire un plug-in
Enregistrer un plug-in
Service asynchrone dans Dataverse
Exemple : Plug-in personnalisé compatible Azure
Exemple : Activité de workflow personnalisée compatible Azure
Azure Functions
Table ServiceEndpoint
Table SdkMessageProcessingStepBase
Table AsynchronousOperations
RemoteExecutionContext
IServiceEndpointNotificationService

Notes

Pouvez-vous nous indiquer vos préférences de langue pour la documentation ? Répondez à un court questionnaire. (veuillez noter que ce questionnaire est en anglais)

Le questionnaire vous prendra environ sept minutes. Aucune donnée personnelle n’est collectée (déclaration de confidentialité).