Utilizar webhooks para crear controladores externos de eventos de servidor

Con Microsoft Dataverse puede enviar datos sobre eventos que tienen lugar en el servidor a una aplicación web mediante webhooks. Webhooks es un patrón HTTP ligero para conectar servicios y API web con un modelo de publicación/suscripción. Los remitentes de webhook envían a los receptores notificaciones sobre eventos realizando solicitudes a los extremos de los receptores con información sobre los eventos.

Los webhooks permiten a desarrolladores e ISV integrar datos de Dataverse en su propio código personalizado hospedado en servicios externos. Con el modelo de webhook, puede proteger el extremo usando un encabezado de autenticación o claves de parámetro de string de consulta. Esto es más sencillo que el modelo de autenticación SAS que se puede usar actualmente para la integración de Azure Service Bus.

Cuando decida entre el modelo de webhook y la integración de Azure Service Bus, aquí se muestran algunos elementos a tener en cuenta:

  • Azure Service Bus funciona para un procesamiento de alta escala y proporciona un mecanismo completo de puesta en cola si Dataverse está impulsando muchos eventos.
  • Los webhooks solo pueden escalar hasta el punto en el que su servicio web hospedado pueda manejar los mensajes.
  • Los webhooks permiten pasos sincrónicos y asincrónicos. Azure Service Bus solo permite pasos asincrónicos.
  • Los webhooks envian solicitudes POST con carga JSON y pueden ser consumidos por cualquier lenguaje de programación o aplicación web hospedada en cualquier lugar.
  • Tanto WebHooks como Azure Service Bus se pueden invocar desde un complemento o una actividad personalizada de flujo de trabajo.

Comenzar

El uso de webhooks se divide en tres partes:

  • Crear o configurar un servicio para consumir solicitudes de webhook.
  • Registrar paso de webhook en el servicio de Dataverse o
  • Invocar a un webhook desde un complemento o actividad de flujo de trabajo personalizada.

Empezar registrando un webhook de prueba

Para comprender cómo crear y configurar un servicio para consumir una solicitud de webhook de Dataverse, conviene empezar entendiendo cómo registrar un webhook. Más información: Registrar un webhook

Cuando haya registrado un webhook de ejemplo puede usar un sitio de registro de solicitudes para explorar los datos contextuales que se pasarán. Más información: Probar registro de webhook con sitio de registro de solicitudes

Sugerencia

Completar los pasos para registrar un webhook de prueba y examinar los datos contextual que se pasan le ayudará a hacer más fácil de entender el resto de la información de este tema. Complete estos pasos y vuelva a este tema.

Crear o configurar un servicio para consumir solicitudes de webhook

Los webhooks son simplemente un patrón que se puede aplicar mediante una gran variedad de tecnologías. No es necesario utilizar marcos, plataformas o lenguajes de programación. Utilice las capacidades y conocimiento que tiene para proporcionar la solución apropiada.

Azure Functions proporciona una forma excelente de crear una solución mediante webhooks, pero no es un requisito. Esta sección no proporcionará instrucciones para una solución específica, sino que describirá los datos que se transmitirán a su servicio y permitirá añadir valor a este.

Como se muestra en Probar el registro de webhook con un sitio de registro de solicitud, puede registrar un paso de webhook de prueba y usar el sitio de registro de solicitud para capturar los tipos específicos de datos que pueda procesar su aplicación.

Datos que se transmiten al servicio

Existen tres tipos de datos en la solicitud: cadena de consulta, datos de encabezado y cuerpo de la solicitud.

Cadena de consulta

El único tipo de datos que se transmitirá como una cadena de consulta pueden ser los valores de autenticación transmitidos si el webhook está configurado para usar las opciones WebhookKey o HttpQueryString como se describe en las Opciones de autenticación.

Datos del encabezado

Si selecciona la opción de autenticación HttpHeader, deberá usar los pares de clave/valor que requiera su servicio.

En la siguiente tabla aparecen otros datos que es posible que encuentre transmitidos a su servicio:

Tecla Descripción del valor
x-ms-dynamics-organization El nombre de dominio del entorno que envía la solicitud
x-ms-dynamics-entity-name El nombre lógico de la tabla transmitida a los datos de contexto de ejecución.
x-ms-dynamics-request-name Nombre del evento para el que se registró el paso de webhook.
x-ms-correlation-request-id Identificador único para rastrear cualquier tipo de extensión. La plataforma utiliza esta propiedad para la prevención de bucle infinito. En la mayoría de casos, esta propiedad se puede omitir. Este valor se puede utilizar cuando se trabaja con el soporte técnico, ya que se puede usar para consultar telemetría y comprender lo que ocurrió durante toda la operación.
x-ms-dynamics-msg-size-exceeded Se envía solo cuando el tamaño de la carga HTTP supera los 256 KB.

Cuerpo de la solicitud

El cuerpo contendrá una cadena que representa el valor JSON de una instancia de la clase RemoteExecutionContext. Estos son los mismos datos que se transmiten a las integraciones de Azure Service Bus.

El servicio que cree debe analizar estos datos para extraer los elementos relevantes de información para que su servicio proporcione su función. Cómo decidir analizar estos datos depende de la tecnología que utilice y de sus preferencias.

El siguiente es un ejemplo de los datos JSON serializados transmitidos para un paso registrado con las propiedades siguientes:

Propiedad Descripción
Mensaje Actualizar
Entidad principal contacto
Entidad secundaria ninguno
Atributos de filtro firstname,lastname
Ejecutar en contexto de usuario Usuario que llama
Pedido de ejecución 1
Fase de canalización de eventos de ejecución PostOperation
Modo de ejecución Asincrónico
{
    "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"
}

Importante

Cuando el tamaño de toda la carga HTTP supera 256 KB, se incluirá el encabezado x-ms-dynamics-msg-size-exceeded y se eliminarán las siguientes propiedades RemoteExecutionContext:

Algunas operaciones no incluyen estas propiedades.

Invocar a un webhook de la actividad de un flujo de trabajo o del complemento

Puesto que un webhook es un tipo de extremo de servicio, también puede llamarlo sin registrar un paso con una actividad de flujo de trabajo o del complemento del mismo modo que puede para un extremo de Azure Service Bus. Es necesario proporcionar el ServiceEndpointId para la interfaz IServiceEndpointNotificationService. Consulte los siguientes ejemplos de Azure Service Bus para obtener más información:

Solución de problemas de registros de webhook

Los webhooks son relativamente simples. El servicio enviará la solicitud y evaluará la respuesta. El sistema no puede analizar los datos que se devuelve con el cuerpo de la respuesta, solo buscará en el valor StatusCode de respuesta.

El tiempo de espera es de 60 segundos. En general, esto fallará si no se devuelve ninguna respuesta antes del tiempo de espera o si el valor StatusCode de respuesta no se encuentra en el intervalo 2xx para indicar éxito. La excepción se produce cuando el error devuelto se encuentra en la tabla siguiente:

Código de estado Descripción
502 Puerta de enlace incorrecta
503 Servicio no disponible
504 Tiempo de espera de puerta de enlace

Estos errores indican un problema de red que se puede resolver con otro intento. El servicio webhook hará un intento más solo cuando se devuelvan estos códigos de error.

Webhooks asincrónicos

Si su webhook se registra para ejecutarse de forma asincrónica, puede examinar el trabajo del sistema para conocer los detalles del error. Más información: La consulta produjo error en trabajos asincrónicos para un paso determinado

Webhooks sincrónicos

Cuando decida usar un modo de ejecución sincrónica cualquier error se notificará al usuario de la aplicación con un diálogo de error Extremo no disponible que informa al usuario de que el extermo del servicio webhook puede estar configurado incorrectamente o no está disponible. El diálogo le permitirá descargar un archivo de registro para obtener los detalles de cualquier error.

Nota

Cualquier webhook registrado para un paso sincrónico enviará los datos del contexto de ejecución al punto de conexión configurado de inmediato. Si se produce un error después de enviar la solicitud, la operación de datos se revertirá pero la solicitud enviada al punto de conexión configurado no se podrá recuperar.

Pasos siguientes

Registrar un webhook
Probar el registro de webhook con un sitio de registro de solicitud

Consultar también

Escribir un complemento
Registrar un complemento
Servicio asincrónico en Dataverse
Ejemplo: complemento personalizado con Azure
Ejemplo: actividad personalizada de flujo de trabajo basada en Azure
Funciones de Azure
Tabla ServiceEndpoint
Tabla SdkMessageProcessingStep
Tabla AsynchronousOperations
RemoteExecutionContext
IServiceEndpointNotificationService

Nota

¿Puede indicarnos sus preferencias de idioma de documentación? Realice una breve encuesta. (tenga en cuenta que esta encuesta está en inglés)

La encuesta durará unos siete minutos. No se recopilan datos personales (declaración de privacidad).