Microsoft Graph 中 Outlook 资源的更改通知

Microsoft 图形 API允许你订阅对资源的更改(包括资源的创建、更新或删除),并通过 Webhook 接收通知。 订阅 指定要监视特定资源的所需更改类型,并包含终结点用于接收这些更改通知的 URL。

设置订阅可减少开销,否则需要查询和比较资源来推断任何更改。 可以选择在订阅请求中指定要加密的资源数据,并在通知中包含已更改的资源数据,从而保存单独的后续 API 调用以获取资源有效负载。

对于所有应用程序,每个邮箱的 Outlook 资源活动订阅数上限为 1000 个。 你可以订阅邮箱中的联系人、事件或邮件中的更改。

订阅联系人、日历或邮件中的更改

若要订阅 Outlook 资源的更改通知,请先创建 订阅

以下 Outlook 资源支持 更改通知 包含或不包含有效负载中资源数据的订阅。

创建订阅

若要 创建订阅,请指定要接收通知的 Outlook 资源和更改类型(创建、更新或删除)。 请参阅示例

请求权限

创建和管理(获取、更新和删除)订阅需要资源的读取范围。 例如,若要获取有关邮件的更改通知,应用需要 Mail.Read 权限。 Outlook 更改通知支持委派和应用程序权限范围。 请注意以下限制:

  • 委托的权限仅支持订阅已登录用户的邮箱内文件夹中的项。 例如,不能使用委托的权限 Calendars.Read 订阅其他用户邮箱中的事件。

  • 订阅共享或委托文件夹中 Outlook 联系人、事件或邮件的更改通知:

    • 使用相应的应用程序权限订阅租户内任何用户的文件夹或邮箱中项目的更改。
    • 请勿使用 Outlook 共享权限 (Contacts.Read.Shared、Calendars.Read.Shared、Mail.Read.Shared 及其读/写权限) ,因为它们 不支持 订阅共享或委派文件夹中项目的更改通知。

根据资源,使用下表中指定的最低权限调用此 API。

资源 支持的资源路径 委派(工作或学校帐户) 委派(个人 Microsoft 帐户) 应用程序
联系人 对用户邮箱中的所有个人联系人更改:
/me/contacts
/users/{id}/contacts
对用户 contactFolder 中的联系人的更改:
/users/{id}/contactFolders/{id}/contacts
Contacts.Read Contacts.Read Contacts.Read
event 对用户邮箱中的所有事件更改:
/me/events
/users/{id}/events
Calendars.Read Calendars.Read Calendars.Read
邮件 对用户邮箱中的所有邮件更改:
/me/messages
/users/{id}/messages
对用户 mailFolder 中的邮件的更改:
/users/{id}/mailFolders/{id}/messages
Mail.ReadBasic、Mail.Read Mail.ReadBasic、Mail.Read Mail.ReadBasic、Mail.Read

在通知有效负载中包含资源数据

若要将资源数据包含在更改通知中,除了创建订阅时通常包含的属性外, 还必须 指定以下属性:

  • includeResourceData:将此属性设置为 true 以显式请求资源数据。

  • resource:此属性指定资源 URL。 请确保使用 $select 查询参数显式指定要包含在通知有效负载中的 Outlook 资源属性。

    注意

    除了 singleValueExtendedPropertiesmultiValueExtendedProperties 之外,请勿在 URL 中包含 $top$skip$orderby$select=Body,UniqueBody$expand

  • encryptionCertificate: 此属性仅包含 Microsoft Graph 用于加密资源数据的公钥。 保留相应的私钥,以解密内容

  • encryptionCertificateId: 此属性是你自己的证书标识符。 使用此 ID 在每个更改通知中匹配用于解密的证书。

请参阅示例,以订阅使用邮件资源的资源数据的更改通知。

优化通知的条件

可以使用 $filter 查询参数进一步优化通知的条件。 请参阅示例

$filter的一个常见应用是在特定资源属性发生更改时收到通知。 例如,可以使用 $filter 订阅文件夹中的未读邮件(isRead 属性为 false),并包括所有更改类型:

  • 将邮件添加到文件夹或将其标记为未读将触发 Created 通知。
  • 读取文件夹中的邮件或将其标记为已读将触发 Deleted 通知。
  • 更改文件夹中邮件资源的任何属性(isRead除外)将触发Updated通知。

如果在创建订阅时不使用 $filter

  • 将邮件添加到文件夹将生成 Created 通知。
  • 读取文件夹中的邮件、将邮件标记为已读或更改该文件夹中邮件的任何其他属性将生成 Updated 通知。
  • 删除邮件将生成 Delete 通知。

订阅生命周期通知

Outlook 联系人事件邮件资源也支持订阅生命周期通知。 删除应用订阅或错过某些更改通知时,需要生命周期通知。 应用程序应实现逻辑以进行检测并恢复丢失的内容,以及恢复连续更改通知流程。 若要了解详细信息,请参阅订阅生命周期通知

跟踪订阅生命周期

请确保在订阅过期之前延长订阅。 没有 Outlook 资源数据的订阅的最大生命周期为 4230 分钟(不到 3 天),有资源数据的订阅的最大生命周期为 1 天。

如果失去之前为订阅授予的权限,并且此时订阅过期,请再次请求权限以创建新订阅。

接收通知有效负载

根据订阅,通知可能包含资源数据。 具有资源数据的订阅允许你获取资源有效负载和通知,从而避免单独的 API 调用以获取更改的资源数据的开销。

接收包含资源数据的通知

下面是包含邮件资源的资源数据的通知有效负载示例。 resourceresourceData 属性对应于触发通知的消息实例。 使用 encryptedContent 属性可解密资源数据。

{
    "value": [
      {
        "subscriptionId": "dfd11b2f-8222-4189-9545-4205c95d6235",
        "subscriptionExpirationDateTime": "2021-12-31T16:16:44.9907405+05:30",
        "changeType": "created",
        "resource": "Users('722effaf-0433-4272-9ac4-d5ec11c3cd77')/messages('AAMkAGUwNjQ4ZjIxLTQ3Y2Y8AAA=')",
        "clientState": "<<--SpecifiedClientState-->>",
        "tenantId": "<<--TenantForWhichNotificationWasSent-->>",
        "encryptedContent": {
          "data": "<<--EncryptedContent-->>",
          "dataKey": "<<--EnryptedDataKeyUsedForEncryptingContent-->>",
          "dataSignature": "Qw/9ubWeUYJPWWXvNiGgct2FkNG2MXTRm/BLUpJM66k=",
          "encryptionCertificateId": "<<--IdOfTheCertificateUsedForEncryptingDataKey-->>",
          "encryptionCertificateThumbprint": "<<--ThumbprintOfTheCertificateUsedForEncryptingDataKey-->>"
        },
        "resourceData": {
          "@odata.type": "#microsoft.graph.message",
          "@odata.id": "Users('722effaf-0433-4272-9ac4-d5ec11c3cd77')/messages('AAMkAGUwNjQ4ZjIxLTQ3Y2Y8AAA=')",
          "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAGDUR8n\"",
          "id": "AAMkAGUwNjQ4ZjIxLTQ3Y2Y8AAA="
        }
      }
    ]
    "validationTokens": ["<<--ValidationTokens-->>"]
}

有关如何验证令牌和解密负载的详细信息,请参阅设置包含资源数据的更改通知

下面是解密的通知有效负载的示例。 解密的有效负载符合 Outlook 邮件架构。 该有效负载类似于GET 邮件操作返回的负载。 但是,通知有效负载仅包含在订阅的资源属性中使用 $select 参数指定的属性。 其他 Outlook 资源(如联系人事件)的通知有效负载遵循其各自的架构。

{
    "receivedDateTime@odata.type":"#DateTimeOffset",
    "receivedDateTime":"2021-12-30T10:53:35Z",
    "subject":"TEST MESSAGE FOR RICH NOTIFICATIONS",
    "bodyPreview":"Hello,\r\n\r\nWhat\u2019s up?\r\n\r\nThanks\r\nMegan",
    "importance@odata.type":"#microsoft.graph.importance",
    "importance":"normal",
    "from": {
        "@odata.type":"#microsoft.graph.recipient",
        "emailAddress": {
            "@odata.type":"#microsoft.graph.emailAddress",
            "name":"Megan Brown",
            "address":"Megan.Brown@microsoft.com"
        }
    }
}

接收不含资源数据的通知

不含资源数据的通知为你提供了足够的信息来进行 GET 调用以获取资源。 订阅不含资源数据的通知不需要加密证书,因为不会发送实际资源数据。

下一个示例显示与 Outlook 邮件资源对应的通知有效负载。 它包括 resourceresourceData 属性,这些属性表示触发通知的资源。 使用 resource@odata.id 属性对 Microsoft Graph 进行调用以获取资源的有效负载。

注意

GET 调用始终返回资源的当前状态。 如果在发送通知和检索资源之间更改了资源,则操作会在检索时返回资源的状态。

"value": [
 {
   "subscriptionId": "c6126aa3-0ed8-412f-a988-71e6cee627c4",
   "subscriptionExpirationDateTime": "2022-01-02T03:12:18.2257768+05:30",
   "changeType": "created",
   "resource": "Users/622eaaff-0683-4862-9de4-f2ec83c2bd98/Messages/AAMkAGUwNjQ4ZjIxAAA=",
   "resourceData": {
     "@odata.type": "#Microsoft.Graph.Message",
     "@odata.id": "Users/622eaaff-0683-4862-9de4-f2ec83c2bd98/Messages/AAMkAGUwNjQ4ZjIAAA=",
     "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAGDUUXn\"",
     "id": "AAMkAGUwNjQ4ZjIxAAA="
   },
   "clientState": "<<--SpecifiedClientState-->>",
   "tenantId": "<<--TenantForWhichNotificationWasSent-->>"
 }
]

示例

示例 1:创建订阅以在用户收到新消息时获取不含资源数据的更改通知

以下示例请求在用户邮箱中创建邮件的通知。

请求

POST https://graph.microsoft.com/v1.0/subscriptions
Content-type: application/json
{
    "changeType": "created",
    "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
    "resource": "users/622eaaff-0683-4862-9de4-f2ec83c2bd98/messages",
    "expirationDateTime": "2021-07-07T21:42:18.2257768+00:00",
    "clientState": "secretClientState"
}

响应

以下示例显示了相应的响应。

注意:为了提高可读性,可能缩短了此处显示的响应对象。

HTTP/1.1 201 Created
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
    "id": "5522bd62-7c96-4530-85b0-00b916f6151a",
    "resource": "users/622eaaff-0683-4862-9de4-f2ec83c2bd98/messages",
    "applicationId": "507c3b9a-67b8-463d-88a2-15a8cefb2111",
    "changeType": "created",
    "clientState": "secretClientState",
    "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
    "notificationQueryOptions": null,
    "notificationContentType": null,
    "lifecycleNotificationUrl": null,
    "expirationDateTime": "2022-01-01T21:42:18.2257768Z",
    "creatorId": "a4c7bd34-4f3b-46b7-a25d-b63c1e2a2842",
    "includeResourceData": null,
    "latestSupportedTlsVersion": "v1_2",
    "encryptionCertificate": null,
    "encryptionCertificateId": null,
    "notificationUrlAppId": null
}

示例 2:创建订阅以在用户收到新消息时获取包含资源数据的更改通知

以下示例订阅在用户邮箱中创建邮件的带有资源数据的通知。 使用 $select 查询参数指定要包含在通知有效负载中的邮件资源的属性。

请求

POST https://graph.microsoft.com/v1.0/subscriptions
Content-type: application/json
{
    "changeType": "created",
    "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
    "resource": "users/622eaaff-0683-4862-9de4-f2ec83c2bd98/messages?$select=Subject,bodyPreview,importance,receivedDateTime,from",
    "expirationDateTime": "2022-01-01T21:42:18.2257768+00:00",
    "clientState": "secretClientValue",
    "includeResourceData": true,
    "encryptionCertificate": "MIIDMzCCAhugAwIBAgIQE7D+++Dk1hKQBqWA==",
    "encryptionCertificateId": "testCertificateId"
}

响应

以下示例显示了相应的响应。

注意:为了提高可读性,可能缩短了此处显示的响应对象。

HTTP/1.1 201 Created
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
    "id": "178eec5f-cf3c-4e7e-8a9c-8640deb5b5c5",
    "resource": "users/622eaaff-0683-4862-9de4-f2ec83c2bd98/messages?$select=Subject,bodyPreview,importance,receivedDateTime,from",
    "applicationId": "507c3b9a-67b8-463d-88a2-15a8cefb2111",
    "changeType": "created",
    "clientState": "secretClientValue",
    "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
    "notificationQueryOptions": null,
    "notificationContentType": null,
    "lifecycleNotificationUrl": null,
    "expirationDateTime": "2022-01-01T12:32:35.1582696Z",
    "creatorId": "a4c7bd34-4f3b-46b7-a25d-b63c1e2a2842",
    "includeResourceData": true,
    "latestSupportedTlsVersion": "v1_2",
    "encryptionCertificate": "MIIDMzCCAhugAwIBAgIQE7D+++Dk1hKQBqWA==",
    "encryptionCertificateId": "testCertificateId",
    "notificationUrlAppId": null
}

示例 3:根据条件创建订阅以获取包含消息的资源数据的更改通知

下面的示例使用资源数据订阅在草稿文件夹中创建包含一个或多个附件且具有高重要性的邮件的通知。

请求

POST https://graph.microsoft.com/v1.0/subscriptions
Content-type: application/json
{
    "changeType": "created",
    "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
    "resource": "me/mailfolders('Drafts')/messages?$select=Subject,bodyPreview&$filter=hasAttachments eq true AND importance eq 'High'",
    "expirationDateTime": "2022-01-01T21:42:18.2257768+00:00",
    "clientState": "secretClientValue",
    "includeResourceData": true,
    "encryptionCertificate": "MIIDMzCCAhugAwIBAgIQE7D+++Dk1hKQBqWA==",
    "encryptionCertificateId": "testCertificateId"
}

响应

以下示例显示了相应的响应。

注意:为了提高可读性,可能缩短了此处显示的响应对象。

HTTP/1.1 201 Created
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
    "id": "239dbc5f-cf3c-4e7e-8c9c-3340abc5b5c5",
    "resource": "me/mailfolders('Drafts')/messages?$select=Subject,bodyPreview&$filter=hasAttachments eq true AND importance eq 'High'",
    "applicationId": "507c3b9a-67b8-463d-88a2-15a8cefb2111",
    "changeType": "created",
    "clientState": "secretClientValue",
    "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
    "notificationQueryOptions": null,
    "notificationContentType": null,
    "lifecycleNotificationUrl": null,
    "expirationDateTime": "2022-01-20T12:32:35.1582696Z",
    "creatorId": "a4c7bd34-4f3b-46b7-a25d-b63c1e2a2842",
    "includeResourceData": true,
    "latestSupportedTlsVersion": "v1_2",
    "encryptionCertificate": "MIIDMzCCAhugAwIBAgIQE7D+++Dk1hKQBqWA==",
    "encryptionCertificateId": "testCertificateId",
    "notificationUrlAppId": null
}