SharePoint Webhook 示例参考实现
SharePoint 模式和做法 (PnP) 参考实现显示如何在应用程序中使用 SharePoint webhook。 webhook 以企业就绪方式实现,将各种 Microsoft Azure 组件(如 Azure Web 作业、Azure SQL Server 和 Azure 存储队列)用于异步 Web 作业通知处理。
此参考实现仅适用于 SharePoint 列表 Webhook。
还可以通过观看 Microsoft 365 平台社区 (PnP) YouTube 频道上的视频来按照以下步骤操作:
适用于 Office 365 多租户 (MT)。
Microsoft Azure 用于托管实现 SharePoint Webhook 所需的各种组件。
用于参考实现的源代码和其他材料有两个版本:
- SharePoint 提供程序托管的应用程序版本
- Office 365 Azure AD 应用程序,可在 SharePoint 开发人员示例 GitHub 存储库中找到。
部署参考实现
此应用程序展示如何管理专门针对 SharePoint 列表的 Webhook。 它还包含了 Webhook 服务终结点的参考实现,可在 Webhook 项目中重复使用。
部署指南
- SharePoint Webhook 参考实现部署指南 列出了用于部署 SharePoint 提供程序托管的参考实现的部署步骤。
- 要部署 Office 365 Azure AD 应用程序,请使用 SharePoint Webhook Azure AD 参考实现部署指南 中说明的步骤,其中介绍了如何将 Web API 函数用作 Webhook 服务。
- 如果你对使用 Azure Functions 更感兴趣,请参阅 Azure Functions 指南,详细了解如何在此参考实现中使用 Azure Functions。
Webhook 简介
webhook 通知该应用程序 SharePoint 中应用程序需要监视的的更改。 应用程序不再需要定期轮询更改。 通过 Webhook,只要进行了更改就会通知应用程序(推送模型)。 Webhook 并不特定于 Microsoft。 它们是一种通用的 Web 标准,同时也被其他供应商(例如,WordPress、GitHub、MailChimp 等)采用。
将 Webhook 添加到 SharePoint 列表
此参考实现适用于 SharePoint 列表。 若要将 Webhook 添加到 SharePoint 列表,应用首先需要发送 POST /_api/web/lists('list-id')/subscriptions
请求,创建 Webhook 订阅。 请求包含以下项:
- 标识要添加 Webhook 的列表的有效负载。
- 你的 Webhook 服务 URL 发送通知的位置。
- Webhook 的到期日期。
请求 SharePoint 添加 Webhook 后,SharePoint 将验证是否存在 Webhook 服务终结点。 它会将验证字符串发送到服务终结点。 SharePoint 预计服务终结点在 5 秒钟内返回验证字符串。 如果此过程失败,则会取消 Webhook 的创建。 如果服务已部署,此操作将生效,并且 SharePoint 会针对应用程序最初发送的 POST 请求返回 HTTP 201 消息。 响应中的有效负载包含新的 Webhook 订阅的 ID。
请查看参考实现,将会发现所有 Webhook CRUD 操作都合并到了 SharePoint.WebHooks.Common 项目的 WebHookManager 类中。 添加 Webhook 的操作通过使用 AddListWebHookAsync 方法完成:
/// <summary>
/// This method adds a webhook to a SharePoint list. Note that you need your webhook endpoint being passed into this method to be up and running and reachable from the internet
/// </summary>
/// <param name="siteUrl">Url of the site holding the list</param>
/// <param name="listId">Id of the list</param>
/// <param name="webHookEndPoint">Url of the webhook service endpoint (the one that will be called during an event)</param>
/// <param name="accessToken">Access token to authenticate against SharePoint</param>
/// <param name="validityInMonths">Optional webhook validity in months, defaults to 3 months, max is 6 months</param>
/// <returns>subscription ID of the new webhook</returns>
public async Task<SubscriptionModel> AddListWebHookAsync(string siteUrl, string listId, string webHookEndPoint, string accessToken, int validityInMonths = 3)
{
// webhook add code...
}
调用 SharePoint 时,需要提供身份验证信息,在本示例中,使用的是包含访问令牌的持有者身份验证头。 若要获取访问令牌,请通过 ExecutingWebRequest 事件处理程序拦截令牌:
ClientContext cc = null;
// Create SharePoint ClientContext object...
// Add ExecutingWebRequest event handler
cc.ExecutingWebRequest += Cc_ExecutingWebRequest;
// Capture the OAuth access token since we want to reuse that one in our REST requests
private void Cc_ExecutingWebRequest(object sender, WebRequestEventArgs e)
{
this.accessToken = e.WebRequestExecutor.RequestHeaders.Get("Authorization").Replace("Bearer ", "");
}
SharePoint 调用 Webhook 服务
当 SharePoint 检测到为其创建 Webhook 订阅的列表中的更改时,SharePoint 将调用服务终结点。 在 SharePoint 中查看有效负载时,请注意以下属性非常重要:
属性 | 说明 |
---|---|
Webhook 订阅 ID。 如果要更新 Webhook 订阅(例如,延长 Webhook 到期日期),则需要此 ID。 | |
发生更改的列表 ID。 | |
托管会发生变化的资源的网站的服务器相对 URL。 |
注意
SharePoint 只发送通知来提示已发生更改,但并不包含实际变化内容。 由于获取有关网站和列表发生更改的信息,这意味着可以使用相同的服务终结点来处理多个网站和列表中的 webhook 事件。
调用服务时,务必使服务在 5 秒内响应 HTTP 200 消息。 稍后本文将对响应时间进行介绍,但实质上,这要求异步处理通知。 在此参考实现中,将通过使用 Azure Web 作业和 Azure 存储队列来执行此操作。
捕捉服务需要对其进行响应的更改
上一步中已调用服务终结点,但是 SharePoint 只提供了有关更改发生位置的信息,并没有提供实际更改内容。 若要了解更改的内容,需要使用 SharePoint GetChanges()
API,如下图所示。
可以在 SharePoint.WebHooks.Common 项目的 ChangeManager 类中的 ProcessNotification 方法中了解有关 GetChanges()
实现的详细信息。
为避免重复获得相同的更改,务必通知 SharePoint 要从中进行更改的点。 此操作通过传递 changeToken 完成,这也意味着服务终结点需要保持最后一次使用的 changeToken,以便它在下一次调用服务终结点时使用。
对于更改,需要注意以下几个要点:
- SharePoint 不会实时调用服务:当具有 Webhook 的列表发生更改时,SharePoint 将通过队列列出 Webhook 调用。 此队列每分钟读取一次,并调用适当的服务终结点。 请务必这样批处理请求。 例如,如果一次批量上载 1000 条记录,批处理可防止 SharePoint 调用终结点 1000 次。 因此,终结点只会调用一次,但如果调用
GetChanges()
方法,将会获得 1000 个需要处理的更改事件。 - 为保证立即响应,不管更改的数目是多少,服务终结点的工作负载务必以异步方式运行。 在此参考实现中,我们利用了 Azure 的强大功能:该服务将序列化传入的有效负载并将其存储在 Azure 存储队列中,同时 Azure Web 作业持续运行,并检查队列中的消息。 队列中出现消息时,Web 作业将对其进行处理,同时以异步方式执行逻辑。
完整的端到端流
下图描述了完整的端到端 Webhook 流。
- 应用程序创建一个 Webhook 订阅。 执行此操作时,它将从为其创建 Webhook 的列表中获取当前 changeToken。
- 应用程序会将 changeToken 保留在永久性存储区中,如本示例中的 SQL Azure。
- SharePoint 中发生更改,SharePoint 调用服务终结点。
- 服务终结点将序列化通知请求,并将其存储在存储队列中。
- Web 作业在队列中查看此消息,并启动消息处理逻辑。
- 消息处理逻辑从持久性存储区中检索上次使用的更改令牌。
- 消息处理逻辑使用
GetChanges()
API 来确定更改内容。 - 在处理好返回的更改后,现在应用程序将基于这些更改执行所需的操作。
- 最后,应用程序将保留上次检索的 changeToken,以便下次不会收到已处理的更改。
使用 Webhook 续订
默认情况下,Webhook 订阅设置的过期时间为 6 个月,或在创建时指定一个日期。 通常需要 Webhook 的可用时间更长。 以下部分中所述的模式非常适用于增加 Webhook 订阅的生存期。 第一个模式为轻量型,第二个稍微复杂一些,而且需要托管其他 Web 作业。
基本模型
在服务接收通知时,它还获取有关订阅生存期的信息。 如果订阅即将过期,只需在通知处理逻辑中延长订阅的生存期。 此模型在此参考实现中实现,并适用于大多数情况。 但是,如果在为其创建 Webhook 订阅的列表上连续 6 个月未发生更改,在这种情况下,将不会延长 Webhook 订阅的生存期并会将此订阅删除。
可靠但更复杂的模型
每周创建一个 web 作业,以从持久性存储区中读取所有订阅 ID。 每次逐一扩展找到的订阅。
注意
此 Web 作业不属于此参考实现。
可以通过使用 PATCH /_api/web/lists('list-id')/subscriptions(‘subscriptionID’)
REST 调用,完成 SharePoint 列表 Webhook 的实际续订。
在参考实现中,Webhook 的更新在 SharePoint.WebHooks.Common 项目的 WebHookManager 类中实现。
通过使用 UpdateListWebHookAsync 方法完成 Webhook 更新:
/// <summary>
/// Updates the expiration datetime (and notification URL) of an existing SharePoint list webhook
/// </summary>
/// <param name="siteUrl">Url of the site holding the list</param>
/// <param name="listId">Id of the list</param>
/// <param name="subscriptionId">Id of the webhook subscription that we need to update</param>
/// <param name="webHookEndPoint">Url of the webhook service endpoint (the one that will be called during an event)</param>
/// <param name="expirationDateTime">New webhook expiration date</param>
/// <param name="accessToken">Access token to authenticate against SharePoint</param>
/// <returns>true if successful, exception in case something went wrong</returns>
public async Task<bool> UpdateListWebHookAsync(string siteUrl, string listId, string subscriptionId, string webHookEndPoint, DateTime expirationDateTime, string accessToken)
{
// webhook update code...
}
调试 Webhook
由于 SharePoint 正在调用 Webhook 服务终结点,终结点必须可通过 SharePoint 访问。 这使得开发和调试更加复杂一些。 下面是可用来使你的生活更加简单的一些策略:
- 在初始开发过程中,为服务处理逻辑提供你自己的序列化有效负载。 这使它能完全测试处理逻辑,而无需部署服务终结点(甚至无需配置 Webhook)。
- 如果你对 Azure 资源具有访问权限,则可以通过使用调试版本并配置用于调试的 Azure 应用服务将你的终结点部署到 Azure。 使用此操作可以设置远程断点,然后使用 Visual Studio 执行远程调试。
- 如果不想在开发期间部署服务,则需要对服务使用安全隧道。 也就是说,指示 SharePoint 通知服务位于共享公共终结点上。 在客户端中,安装连接到此共享公共服务的组件。这样一来,只要调用公共终结点,就会通知客户端组件,并向在 localhost 上运行的服务推送有效负载。 ngrok 是此类安全隧道工具的实现,可用于在本地调试 Webhook 服务。