获取文件夹中邮件的增量更改

通过 Delta 查询,可通过一系列的 delta 函数调用来查询文件夹中邮件的添加、删除或更新。 借助 Delta 数据,能够维护和同步本地存储的用户邮件,而无需每次从服务器中提取整组用户邮件。

在本地存储中同步消息项可以使用 增量 查询进行初始完全同步和后续增量同步。 通常,你会对文件夹中的所有邮件执行初始完全同步, (例如,用户的收件箱) ,然后定期获取该文件夹的增量更改。

若要仅获取特定类型的增量更改(自初始同步后创建、更新或删除的邮件),请对文件夹中的所有邮件执行初始一轮同步,然后在后续轮次中获取特定所需类型的增量更改。 在初始 增量 请求中指定所需的更改类型作为查询选项;Microsoft Graph 自动将任何 OData 和自定义查询选项编码到 @odata.nextLink 响应中提供的 或 @odata.deltaLink 中。

跟踪文件夹中的邮件更改

Delta 查询对每个文件夹分别执行操作。 为跟踪文件夹层次结构中邮件的更改,需要分别跟踪每个文件夹。

跟踪邮件文件夹中的邮件更改通常需要使用 delta 函数按轮发出一个或多个 GET 请求。 初始 GET 请求非常类似于获取邮件,区别在于要添加 delta 函数:

GET https://graph.microsoft.com/v1.0/me/mailFolders/{id}/messages/delta

使用 delta 函数的 GET 请求返回以下任一内容:

  • @odata.nextLink(包含具有 delta 函数调用和 skipToken 的 URL),或
  • @odata.deltaLink(包含具有 delta 函数调用和 deltaToken 的 URL)。

这些令牌是客户端不透明的 状态令牌 。 若要继续执行一轮更改跟踪,请将从最后一个 GET 请求返回的 URL 复制并应用于同一文件夹的下一个 delta 函数调用。 响应中返回的 @odata.deltaLink 表示当前一轮更改跟踪已完成。 可以保存 @odata.deltaLink URL,并在开始下一轮时使用。

本文的其余部分包括 2 个示例:

  • 请参阅 示例 1 ,了解如何使用 @odata.nextLink@odata.deltaLink URL。
  • 请参阅 示例 2 ,了解如何以增量方式获取自初始轮次以来创建的消息。

在邮件的增量查询中使用查询参数

  • 像在任何 GET 请求中一样,你可以使用 $select 查询参数以仅指定获取最佳性能所需的属性。 始终 id 返回 属性。
  • 对于邮件,Delta 查询支持 $select$top$expand
  • 提供对 $filter$orderby 的有限支持:
    • 唯一支持的 $filter 表达式是 $filter=receivedDateTime+ge+{value}$filter=receivedDateTime+gt+{value}
    • 在增量查询中应用 $filter 最多仅返回 5000 个邮件。
    • 唯一支持的 $orderby 表达式是 $orderby=receivedDateTime+desc。 如果未包含 $orderby 表达式,则无法保证返回顺序。
  • 不支持 $search

此外,若要仅返回特定类型的更改 (在增量查询的响应中创建、更新或删除) ,可以选择使用自定义查询选项 changeType筛选所需的更改类型。 可能的值为 createdupdateddeleted

GET /me/mailfolders/{id}/messages/delta?changeType=created
GET /me/mailfolders/{id}/messages/delta?changeType=updated
GET /me/mailfolders/{id}/messages/delta?changeType=deleted

可选的请求标头

每个 delta 查询 GET 请求在响应中返回包含一个或多个邮件的集合。 可以视需要指定请求头 Prefer: odata.maxpagesize={x},设置响应中可包含的邮件数上限。

示例 1:同步文件夹中的邮件

以下示例显示了对最初包含 5 个邮件的特定文件夹进行的 2 轮同步。

第一轮包含一系列用于同步文件夹中所有 5 个邮件的 3 个请求:

第一轮后,将删除其中一个邮件,并将其他邮件标记为已读。 第二轮同步仅返回 delta(删除和更新),而无需返回保持不变的其他邮件。

示例:初始请求

在此示例中,指定的文件夹首次同步,因此初始同步请求不包含任何状态令牌。 此轮返回该文件夹中的所有邮件。

第一个请求指定以下内容:

  • $select 参数用于在响应中返回每个邮件的 subjectsenderisRead 属性。
  • 可选的请求标头odata.maxpagesize,一次返回两封邮件。
GET https://graph.microsoft.com/v1.0/me/mailfolders/AQMkADNkNAAAgEMAAAA/messages/delta?$select=subject,sender,isRead HTTP/1.1
Prefer: odata.maxpagesize=2

示例第一个响应

响应包括两条消息和一个 @odata.nextLink 响应标头。 URL 表示此文件夹中还更多邮件可获取。

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(message)",
  "@odata.nextLink": "https://graph.microsoft.com/v1.0/me/mailfolders('AQMkADNkNAAAgEMAAAA')/messages/delta?$skiptoken=GwcBoTmPuoTQWfcsAbkYM",
  "value": [
    {
      "@odata.type": "#microsoft.graph.message",
      "@odata.etag": "W/\"CQAAABYAAAARn2vdzPFjSbaPPxzjlzOTAAASsKZz\"",
      "subject": "Holiday hours update",
      "isRead": false,
      "sender": {
        "emailAddress": {
          "name": "Dana Swope",
          "address": "danas@contoso.com"
        }
      },
      "id": "AAMkADNkNAAASq35xAAA="
    },
    {
      "@odata.type": "#microsoft.graph.message",
      "@odata.etag": "W/\"CQAAABYAAAARn2vdzPFjSbaPPxzjlzOTAAAEfYB/\"",
      "subject": "Holiday promotion sale",
      "isRead": true,
      "sender": {
        "emailAddress": {
          "name": "Samantha Booth",
          "address": "samanthab@contoso.com"
        }
      },
      "id": "AQMkADNkNAAAVRMKAAAAA=="
    }
  ]
}

示例第二个请求

第二个请求指定上一个响应中返回的 @odata.nextLink URL。 请注意,不再需要像第一个请求一样指定相同的 $select 参数,因为 skipToken URL 中的 @odata.nextLink 已将其编码并包含在内。

GET https://graph.microsoft.com/v1.0/me/mailfolders/AQMkADNkNAAAgEMAAAA/messages/delta?$skiptoken=GwcBoTmPuoTQWfcsAbkYM HTTP/1.1
Prefer: odata.maxpagesize=2

示例第二个响应

第二个响应中返回此文件夹中接下来的 2 封邮件和另一个 @odata.nextLink(表示此文件夹中还有更多邮件可获取)。

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(message)",
  "@odata.nextLink": "https://graph.microsoft.com/v1.0/me/mailfolders('AQMkADNkNAAAgEMAAAA')/messages/delta?$skiptoken=GwcBoTmPKILK4jLH7mAd1lLU",
  "value": [
    {
      "@odata.type": "#microsoft.graph.message",
      "@odata.etag": "W/\"CQAAABYAAAARn2vdzPFjSbaPPxzjlqfdAAAEfYB+\"",
      "subject": "Microsoft Virtual Academy at Contoso",
      "isRead": true,
      "sender": {
        "emailAddress": {
          "name": "Elliot Hyde",
          "address": "elliot-hyde@tailspintoys.com"
        }
      },
      "id": "AQMkADNkNAAAgWkAAAA"
    },
    {
      "@odata.type": "#microsoft.graph.message",
      "@odata.etag": "W/\"CQAAABYAAAARn2vdzPFjSbaPPxzjlzOTAAAEfYB+\"",
      "subject": "New or modified user account information",
      "isRead": true,
      "sender": {
        "emailAddress": {
          "name": "Randi Welch",
          "address": "randiw@contoso.com"
        }
      },
      "id": "AQMkADNkNAAAgWJAAAA"
    }
  ]
}

示例第三个请求

第三个请求继续使用上一个同步请求返回的最新 @odata.nextLink URL。

GET https://graph.microsoft.com/v1.0/me/mailFolders/AQMkADNkNAAAgEMAAAA/messages/delta?$skiptoken=GwcBoTmPKILK4jLH7mAd1lLU HTTP/1.1
Prefer: odata.maxpagesize=2

示例第三个响应(即最终响应)

第三个响应返回文件夹中唯一剩余的邮件,并返回一个 @odata.deltaLink URL,指示此文件夹的同步暂时已完成。 保存并使用 @odata.deltaLink URL 在下一轮中同步同一文件夹

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(message)",
  "@odata.deltaLink": "https://graph.microsoft.com/v1.0/me/mailfolders('AQMkADNkNAAAgEMAAAA')/messages/delta?$deltatoken=GwcBoTmPuoGNlgXgF1nyUNMXY",
  "value": [
    {
      "@odata.type": "#microsoft.graph.message",
      "@odata.etag": "W/\"CQAAABYAAAARn2vdzFPjSbaPPxzjlzOTAAAEfYB+\"",
      "subject": "Fabric CDN now available",
      "isRead": true,
      "sender": {
        "emailAddress": {
          "name": "Jodie Sharp",
          "address": "Jodie.Sharp@contoso.com"
        }
      },
      "id": "AAMkADk0MGFkODE3LWEAAA="
    }
  ]
}

在下一轮中同步同一文件夹中的邮件

@odata.deltaLink使用上一轮中最后一个请求中的 ,只能获取自那时起在该文件夹中添加、删除或更新) 更改 (的邮件。 假如你更喜欢在响应中保持最大页面的同一大小,则下一轮的第一个请求将会如下所示:

GET https://graph.microsoft.com/v1.0/me/mailfolders/AQMkADNkNAAAgEMAAAA/messages/delta?$deltatoken=GwcBoTmPuoGNlgXgF1nyUNMXY HTTP/1.1
Prefer: odata.maxpagesize=2

响应包含 @odata.deltaLink。 这表示现已同步远程邮件文件夹中的所有更改。 已删除一个邮件并更改其他邮件。

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(message)",
  "@odata.deltaLink": "https://graph.microsoft.com/v1.0/me/mailfolders('AQMkADNkNAAAgEMAAAA')/messages/delta?$deltatoken=GwcBoTmPuoGNlgXgF1nyUNMXY",
  "value": [
    {
      "@odata.type": "#microsoft.graph.message",
      "id": "AAMkADk0MGFkODE3LWE4MmYtNDRhOS0Dh_6qB-pB2Sa2pUum19a6YAAKnLuxoAAA=",
      "@removed": {
        "reason": "deleted"
      }
    },
    {
      "@odata.type": "#microsoft.graph.message",
      "@odata.etag": "W/\"CQAAABYAAAARn2vdzPFjSbaPPxzjlzOTAAASsKZz\"",
      "subject": "Holiday hours update",
      "isRead": "true",
      "sender": {
        "emailAddress": {
          "name": "Dana Swope",
          "address": "danas@contoso.com"
        }
      },
      "id": "AAMkADNkNAAASq35xAAA="
    }
  ]
}

示例 2:根据更改类型同步文件夹中的邮件

以下示例显示仅获取自初始同步以来在特定文件夹中创建的邮件。该示例涉及该文件夹的 2 轮同步,最初包含 4 条消息。

第一轮涉及一系列 2 个请求,以同步文件夹中的所有 4 条消息:

第一轮之后,将再创建两条消息,删除一条消息,另一条消息标记为已读。

第二轮同步仅返回更改类型文件夹中 created 的更改 () 创建的两个新邮件,而不返回自上次同步以来保持相同、已删除或更新的其他邮件。

具有指定更改类型的示例初始请求

在此示例中,指定的文件夹首次同步,因此初始同步请求不包含任何状态令牌。 此轮返回该文件夹中的所有邮件。

第一个请求指定以下内容:

  • 一个 changeType 参数,用于仅返回后续增量响应中 创建 的消息。
  • $select 参数用于在响应中返回每个邮件的 subjectsenderisRead 属性。
  • 可选的请求标头odata.maxpagesize,一次返回两封邮件。
GET https://graph.microsoft.com/v1.0/me/mailFolders/AAMkAGUwNc4LTMzAAA=/messages/delta?changeType=created&$select=subject,sender,isRead HTTP/1.1
Prefer: odata.maxpagesize=2

具有指定更改类型的示例初始响应

响应包括两条消息和一个 @odata.nextLink 响应标头。 URL 表示此文件夹中还更多邮件可获取。

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(message)",
    "@odata.nextLink": "https://graph.microsoft.com/v1.0/me/mailFolders/AAMkAGUwNc4LTMzAAA=/messages/delta?$skiptoken=P4lmXpjPRrjB6haAQzSkpK89jYTVD2kVtOeXNRnfYzPbCs",
    "value": [
        {
            "@odata.type": "#microsoft.graph.message",
            "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAId0MBP\"",
            "subject": "Inline Attachments Again",
            "isRead": true,
            "id": "AAMkAGUwNjQ4ZjIxLTQ3Y2YtNDViMi1iZjc4LT2fKdhq8oSKEDSVrdi3lRAAIei5gdAAA=",
            "sender": {
                "emailAddress": {
                    "name": "Megan Brown",
                    "address": "Megan.Brown@contoso.com"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.message",
            "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAId0MBR\"",
            "subject": "RE: Test Outlook TimeZone",
            "isRead": true,
            "id": "AAMkAGUwNjQ4ZjIxLTQ3Y2YtNDViMi1iZjc4LTMKdhq8oSKEDSVrdi3lRAAIei5geAAA=",
            "sender": {
                "emailAddress": {
                    "name": "Megan Brown",
                    "address": "Megan.Brown@contoso.com"
                }
            }
        }
    ]
}

具有指定更改类型的示例第二个请求

第二个请求指定上一个响应中返回的 @odata.nextLink URL。 请注意,它不再需要指定与初始请求中相同的 $selectchangeType 参数,因为 skipToken URL 中的 @odata.nextLink 编码并包含它。

GET https://graph.microsoft.com/v1.0/me/mailFolders/AAMkAGUwNc4LTMzAAA=/messages/delta?$skiptoken=P4lmXpjPRrjB6haAQzSkpK89jYTVD2kVtOeXNRnfYzPbCs HTTP/1.1
Prefer: odata.maxpagesize=2

具有指定更改类型的示例第二个响应

第二个响应返回 文件夹中接下来的 2 条消息和 @odata.deltaLink URL,指示此文件夹的同步暂时已完成。 保存并使用 @odata.deltaLink URL 在下一轮中同步同一文件夹

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(message)",
    "@odata.deltaLink": "https://graph.microsoft.com/v1.0/me/mailFolders/AAMkAGUwNc4LTMzAAA=/messages/delta?$deltatoken=P4lmXpjPRrjB6haAQ_37roqIbjXe66KoV7SMlLH--Jgi8",
    "value": [
        {
            "@odata.type": "#microsoft.graph.message",
            "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAId0MBu\"",
            "subject": "Your preview of the new Briefing email",
            "isRead": true,
            "id": "AAMkAGUwNjQ4ZjIxLTQ3Y2YtNDViMi1iZjc4LTMzNjMwNWM0ZGE2YQBGAAAAAADbrwBIJ",
            "sender": {
                "emailAddress": {
                    "name": "Cortana",
                    "address": "cortana@contoso.com"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.message",
            "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAId0MBw\"",
            "subject": "Char Coding HTML",
            "isRead": true,
            "id": "AAMkAGUwNjQ4ZjIxLTQ3Y2YtNDViMi1iZjc4LTMzNjMwNWM0ZGE2YQBGAAAAAADbrwBA=",
            "sender": {
                "emailAddress": {
                    "name": "John Doe",
                    "address": "John.Doe@contoso.com"
                }
            }
        }
    ]
}

根据指定的更改类型,在下一轮中同步同一文件夹中的邮件

@odata.deltaLink使用上一轮中最后一个响应中的 ,只能获取自那以后添加到该文件夹中的邮件。 假如你更喜欢在响应中保持最大页面的同一大小,则下一轮的第一个请求将会如下所示:

GET https://graph.microsoft.com/v1.0/me/mailFolders/AAMkAGUwNc4LTMzAAA=/messages/delta?$deltatoken=P4lmXpjPRrjB6haAQ_37roqIbjXe66KoV7SMlLH--Jgi8 HTTP/1.1
Prefer: odata.maxpagesize=2

响应包含 @odata.deltaLink。 这表示现已同步远程邮件文件夹中的所有更改。 自上次同步以来添加了两条消息。自上次同步以来更新 & 删除的消息不会在此增量响应中返回。

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(message)",
    "@odata.deltaLink": "https://graph.microsoft.com/v1.0/me/mailFolders/AAMkAGUwNc4LTMzAAA=/messages/delta?$skiptoken=EPuhZPRDHo-r3EBfscYE444fuGSBRV1eXex3JZkLzT9fRM",
    "value": [
        {
            "@odata.type": "#microsoft.graph.message",
            "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAId0MCP\"",
            "subject": "Nested Attachment",
            "isRead": true,
            "id": "AAMkAGUwNjQ4ZjIxLTQ3Y2YtNDViMi1iZjc4LTMzNjMwNWM0ZGE2YQBGAAAAAADbrwBIJ",
            "sender": {
                "emailAddress": {
                    "name": "Patti Fernandez",
                    "address": "PattiF@contoso.com"
                }
            }
        },
        {
            "@odata.type": "#microsoft.graph.message",
            "@odata.etag": "W/\"CQAAABYAAACQ2fKdhq8oSKEDSVrdi3lRAAId0MCN\"",
            "subject": "Attachment Testing",
            "isRead": true,
            "id": "AAMkAGUwNjQ4ZjIxLTQ3Y2YtNDViMi1iZjc4LTMzNjMwNWM0ZGE2YQBGAAAAAADbrwZA=",
            "sender": {
                "emailAddress": {
                    "name": "Patti Fernandez",
                    "address": "PattiF@contoso.com"
                }
            }
        }
    ]
}