使用 Webhook 将 Microsoft Dataverse 服务器事件发送到外部 Web 应用程序。 本文介绍 Dataverse 发送的请求数据,以及 Webhook 如何帮助你为服务器事件生成外部处理程序。
通过使用 Webhook,开发人员和 ISV 可以将 Dataverse 数据与托管在外部服务上的自己的自定义代码集成。 通过使用 Webhook 模型,您可以使用身份验证标头或查询字符串参数键来保护终结点。 此方法比当前可用于 Azure 服务总线集成的 SAS 身份验证模型更简单。
在 Webhook 模型与 Azure 服务总线集成之间做出决定时,请记住以下几点:
- Azure 服务总线适用于大规模处理,如果 Dataverse 正在推送许多事件,则提供完整的队列机制。
- Webhook 只能扩展到托管 Web 服务可以处理消息的程度。
- Webhooks 启用同步和异步操作。 Azure 服务总线仅允许异步步骤。
- Webhook 使用 JSON 有效负载发送 POST 请求,可以由托管在任何地方的任何编程语言或 Web 应用程序使用。
- 可以从插件或自定义工作流活动调用 Webhook 和 Azure 服务总线。
开始
使用Webhook涉及三个部分:
- 创建或配置服务以使用 Webhook 请求。
- 在 Dataverse 服务上注册 Webhook 步骤。
- 从插件或自定义工作流活动调用 webhook。
首先注册一个用于测试的 WebHook
若要了解如何创建和配置服务以使用 Dataverse 中的 WebHook 请求,请首先了解如何注册 WebHook。 有关详细信息,请参阅 注册 WebHook。
注册示例 WebHook 后,使用请求日志记录站点检查传递的上下文数据。 有关详细信息,请参阅使用请求日志记录站点测试 WebHook 注册。
小窍门
完成测试 WebHook 的注册步骤并检查传递的上下文数据,有助于更好地理解本主题中的其余内容。 完成这些步骤并返回到本主题。
创建或配置服务以处理 WebHook 请求
Webhook 只是一种模式,它们可以通过各种技术来实现。 无需使用框架、平台或编程语言。 使用您所掌握的技能和知识来提供适当的解决方案。
Azure Functions 提供了使用 Webhook 交付解决方案的绝佳方法,但这不是一项要求。 本部分不提供特定解决方案的指导。 相反,它描述了 Dataverse 传递给您的服务的数据,这些数据使您的服务能够提供附加价值。
如 测试 WebHook 注册请求日志记录站点中所述,可以注册测试 WebHook 步骤,并使用请求日志记录站点捕获应用程序可以处理的特定数据类型。
传递到服务的数据
请求包括三种类型的数据:查询字符串、标头数据和请求正文。
查询字符串
作为查询字符串传递的唯一数据是身份验证值,如果您将 WebHook 配置为使用 WebhookKey 或 HttpQueryString 选项,如 身份验证选项 中所述。
标头数据
如果选择 HttpHeader 身份验证选项,请使用服务所需的密钥/值对。
预计您的服务将收到以下数据:
| 密钥 | 值描述 |
|---|---|
x-ms-dynamics-organization |
发送请求的环境的域名 |
x-ms-dynamics-entity-name |
在执行上下文数据中传递的表的逻辑名称。 |
x-ms-dynamics-request-name |
WebHook 步骤所注册的事件名称。 |
x-ms-correlation-request-id |
用于跟踪任何类型的扩展的唯一标识符。 平台使用此属性进行无限循环防护。 在大多数情况下,可以忽略此属性。 使用技术支持时,此值可用于查询遥测,以了解整个操作期间发生的情况。 |
x-ms-dynamics-msg-size-exceeded |
仅当 HTTP 有效负载大小超过 256 KB 时发送。 |
请求主体
正文包含一个字符串,表示类实例的 RemoteExecutionContext JSON 值。 这些数据同样被传递到 Azure 服务总线 (Service Bus) 集成。
您创建的服务必须分析此数据,以提取服务的相关信息项来提供服务功能。 选择分析此数据的方式取决于所使用的技术和偏好。
以下示例展示了针对具有以下属性的注册步骤所传递的序列化 JSON 数据:
| 财产 | 说明 |
|---|---|
| 消息 | 更新 |
| 主要实体 | 联系人 |
| 辅助实体 | 无 |
| 筛选属性 | firstname,lastname |
| 在用户的上下文中运行 | 呼叫用户 |
| 执行顺序 | 1 |
| 执行的事件管道阶段 | PostOperation |
| 执行模式 | 异步 |
{
"BusinessUnitId": "e2b9dd85-e89e-e711-8122-000d3aa2331c",
"CorrelationId": "aaaa0000-bb11-2222-33cc-444444dddddd",
"Depth": 1,
"InitiatingUserId": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
"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": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
"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": "00aa00aa-bb11-cc22-dd33-44ee44ee44ee",
"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": "aaaa0000-bb11-2222-33cc-444444dddddd",
"Depth": 1,
"InitiatingUserId": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
"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": "00aa00aa-bb11-cc22-dd33-44ee44ee44ee",
"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": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff"
},
"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": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff"
}
重要
当整个 HTTP 有效负载的大小超过 256 KB 时, x-ms-dynamics-msg-size-exceeded 将包含标头并删除以下 RemoteExecutionContext 属性:
某些操作不包括这些属性。
从插件或工作流活动调用 WebHook
由于 WebHook 是一种服务终结点,因此无需使用插件或工作流活动注册步骤即可调用它。 此方法的工作方式与 Azure 服务总线终结点的工作方式相同。 您需要向 接口提供 IServiceEndpointNotificationService。 有关详细信息,请参阅以下 Azure 服务总线示例:
排查 WebHook 注册问题
Webhook 相对简单。 服务发送请求并评估响应。 系统无法分析使用响应正文返回的任何数据。 它只查看响应 StatusCode 值。
超时为 60 秒。 通常,如果在超时期限之前未返回任何响应,或者响应 StatusCode 值不在 2xx 指示成功的范围内,则操作将失败。 当返回的错误为下表中的错误,将抛出异常:
| 状态码 | 说明 |
|---|---|
502 |
错误的网关 |
503 |
服务不可用 |
504 |
网关超时 |
这些错误表示可能通过另一次尝试解决的网络问题。 仅当返回这些错误代码时,WebHook 服务才会再尝试一次。
异步 Webhook
如果您将 Webhook 注册为异步运行,可以查看系统作业以获取有关错误的详细信息。 有关详细信息,请参阅查询给定步骤的失败异步作业。
同步 Webhook
选择使用同步执行模式时,将向应用程序用户报告失败,并显示终结点不可用错误对话,告知用户 Webhook 服务终结点可能配置不正确或不可用。 该对话允许您下载日志文件来获取有关错误的详细信息。
注释
为同步步骤注册 Webhook 时,它会立即将执行上下文数据发送到配置的终结点。 如果在发送请求后发生错误,则数据操作会回滚,但无法召回发送到配置的终结点的请求。
后续步骤
注册 WebHook
使用请求日志记录站点测试 WebHook 注册
另见
编写插件
注册插件
Dataverse 中的异步服务
示例:Azure 感知自定义插件
示例:Azure 感知自定义工作流活动
Azure Functions
ServiceEndpoint 表
SdkMessageProcessingStep 表
AsynchronousOperations 表
RemoteExecutionContext
IServiceEndpointNotificationService