使用 JSON 批处理合并多个 HTTP 请求

JSON 批处理允许客户端将多个请求合并到单个 JSON 对象和单个 HTTP 调用中,从而减少网络往返并提高效率。 Microsoft Graph 支持将最多 20 个请求批处理到 JSON 对象中。

在本文中,我们将了解 JSON 批处理的基础知识、它的工作原理,以及如何使用它来优化应用程序。

注意

Microsoft Graph 实现 $batchOData URL 路径段 以支持 JSON 批处理。

示例方案

请考虑一个客户端,该客户端希望撰写以下不相关数据的视图:

  • 存储在 OneDrive 中的图像
  • 计划任务列表
  • 组日历

将三个单独请求合并到一个单独的批处理请求中可以使应用程序不受重大网络延迟的影响。

创建批处理请求

创建批处理请求:

  1. 将请求 HTTP 方法指定为 POST。

  2. 指定 URL 终结点,无论它面向v1.0beta还是 Microsoft Graph 版本,并将段$batch追加到 URL。 即 。 https://graph.microsoft.com/v1.0/$batch

  3. 按如下所示定义批处理请求正文:

    1. JSON 批处理请求正文由具有一个必需属性的单个 JSON 对象组成: 请求。 此属性是单个请求的集合。
    2. 对于每个单独的请求,可以传递以下属性。
    属性 说明
    id 必填。 字符串。 一个关联值,用于将单个响应与请求相关联。 此值允许服务器以最有效的顺序处理批处理中的请求。 不区分大小写。 批处理中必须是唯一的,否则批处理请求失败并显示 400 错误代码。
    方法 必需项。 URL 中指定的请求支持的 HTTP 方法。
    url 必需项。 单个请求的相对资源 URL。 因此,尽管绝对 URL 为 https://graph.microsoft.com/v1.0/users,但此 URL 为 /users。
    标头 可选,但在指定 正文 时是必填的。 具有标头的键/值对的 JSON 对象。 例如,当 需要 ConsistencyLevel 标头时,此属性表示为 "headers": {"ConsistencyLevel": "eventual"}。 提供 正文 时,必须包含 Content-Type 标头。
    body 可选。 可以是 JSON 对象或 base64 URL 编码值,例如,当正文是图像时。 当请求中包含 正文 时,标头 对象必须包含 Content-Type 的值。

示例 JSON 批处理请求

在此示例方案中,将构造 JSON 批处理请求。 各个请求不相互依赖,因此可以按任何顺序放入批处理请求中。

POST https://graph.microsoft.com/v1.0/$batch
Accept: application/json
Content-Type: application/json

{
  "requests": [
    {
      "id": "1",
      "method": "GET",
      "url": "/me/memberOf"
    },
    {
      "id": "2",
      "method": "GET",
      "url": "/me/planner/tasks"
    },
    {
      "id": "3",
      "method": "DELETE",
      "url": "/groups/0e226165-c685-41ce-8bfc-df8360ab325d"
    },
    {
      "id": "4",
      "url": "/users/161ab652-cdbc-490d-82a4-0ada1f0db247/getPasswordSingleSignOnCredentials",
      "method": "POST",
      "body": {},
      "headers": {"Content-Type": "application/json"}
    },
    {
      "id": "5",
      "url": "users?$select=id,displayName,userPrincipalName&$filter=city eq null&$count=true",
      "method": "GET",
      "headers": {
        "ConsistencyLevel": "eventual"
      }
    }
  ]
}

处理 JSON 批处理响应

JSON 批处理请求的响应格式不同于请求格式,如下所示:

  • 主 JSON 对象中的属性命名为 响应 而不是 请求。
  • 单独响应可能会按与请求不同的顺序显示。 id 属性可用于关联各个请求和响应。
  • 单个响应具有 status 属性,而不是方法和url。 status 的值是 HTTP 状态代码。
  • 每个响应中的 headers 属性表示服务器返回的标头,例如 Cache-Control 和 Content-Type 标头。

批处理响应中的状态代码通常为 200 或 4xx。 如果批处理请求本身格式不正确,则状态代码为 400。 如果批处理请求可解析,则状态代码为 200。 200批处理响应标头上的状态代码并不指示批处理中的单个请求已成功。 这就是 responses 属性中的每个单个响应都有状态代码的原因。

示例 JSON 批处理响应

对于上一个示例,假设以下响应:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "responses": [
        {
            "id": "1",
            "status": 200,
            "headers": {
                "Cache-Control": "no-cache",
                "x-ms-resource-unit": "1",
                "OData-Version": "4.0",
                "Content-Type": "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8"
            },
            "body": {
                "@odata.context": "https://graph.microsoft.com/beta/$metadata#directoryObjects",
                "@odata.nextLink": "https://graph.microsoft.com/beta/me/memberOf?$top=1&$skiptoken=RFNwdAoAAQAAAAAAAAAAFAAAAI45VMy0CO9Ei1L3Lr1q95UBAAAAAAAAAAAAAAAAAAAXMS4yLjg0MC4xMTM1NTYuMS40LjIzMzEGAAAAAAABURXWGePFEEGbudEn3SOTuQEDAQAAAQAAAAA",
                "value": [
                    {
                        "@odata.type": "#microsoft.graph.directoryRole",
                        "id": "21004afc-7bb2-4fe6-a1e1-074ebd3e52c1",
                        "deletedDateTime": null,
                        "description": "Can manage all aspects of users and groups, including resetting passwords for limited admins.",
                        "displayName": "User Administrator",
                        "roleTemplateId": "fe930be7-5e62-47db-91af-98c3a49a38b1"
                    }
                ]
            }
        },
        {
            "id": "2",
            "status": 403,
            "headers": {
                "Cache-Control": "no-cache",
                "X-ProxyCluster": "wus-001.tasks.osi.office.net",
                "X-OfficeCluster": "wus-001.tasks.osi.office.net",
                "X-Tasks-CorrelationId": "18a8e521-78a4-4129-9b6b-d678116464e7",
                "Content-Type": "application/json"
            },
            "body": {
                "error": {
                    "code": "",
                    "message": "You do not have the required permissions to access this item.",
                    "innerError": {
                        "date": "2025-02-13T10:17:05",
                        "request-id": "93b6f17e-c05d-4f45-ad2a-6665c708d8a0",
                        "client-request-id": "e70c5c1b-8b47-68c0-3171-3d22f5e0bd54"
                    }
                }
            }
        },
        {
            "id": "3",
            "status": 403,
            "headers": {
                "Cache-Control": "no-cache",
                "x-ms-resource-unit": "1",
                "Content-Type": "application/json"
            },
            "body": {
                "error": {
                    "code": "Authorization_RequestDenied",
                    "message": "Insufficient privileges to complete the operation.",
                    "innerError": {
                        "date": "2025-02-13T10:17:06",
                        "request-id": "93b6f17e-c05d-4f45-ad2a-6665c708d8a0",
                        "client-request-id": "e70c5c1b-8b47-68c0-3171-3d22f5e0bd54"
                    }
                }
            }
        },
        {
            "id": "4",
            "status": 405,
            "headers": {
                "Cache-Control": "no-cache",
                "x-ms-resource-unit": "1",
                "Content-Type": "application/json"
            },
            "body": {
                "error": {
                    "code": "Request_BadRequest",
                    "message": "Specified HTTP method is not allowed for the request target.",
                    "innerError": {
                        "date": "2025-02-13T10:21:18",
                        "request-id": "3a3b1bf7-3596-4493-8264-de81e028071f",
                        "client-request-id": "e5f9a304-2796-b7e8-ccce-dd989953ebc4"
                    }
                }
            }
        },
        {
            "id": "5",
            "status": 200,
            "headers": {
                "Cache-Control": "no-cache",
                "x-ms-resource-unit": "1",
                "OData-Version": "4.0",
                "Content-Type": "application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8"
            },
            "body": {
                "@odata.context": "https://graph.microsoft.com/beta/$metadata#users(id,displayName,userPrincipalName)",
                "@odata.count": 36,
                "value": [
                    {
                        "id": "10a1d484-cd1a-4162-a5a4-832370bac356",
                        "displayName": "Lynne Robbins",
                        "userPrincipalName": "LynneR@contoso.com"
                    }
                ]
            }
        }
    ]
}

示例批处理响应中的单个响应的说明

  • 请求 1 和 5 成功,如状态代码所示 200 。
  • 请求 2 和 3 失败并显示 403 状态代码,因为调用方没有所需的权限。
  • 请求 4 失败并显示 405 状态代码,因为在请求的 url 属性中指定的终结点当前仅位于 beta 请求 URL 中,但请求 URL 面向 v1.0 Microsoft Graph 的终结点。 虽然目标 URL 不需要请求正文,但你仍必须指定 标头 和 正文 分析器,其中只有 正文 可以是空对象。

使用 dependsOn 属性对请求进行排序

可以使用 dependsOn 属性指定要按指定顺序执行的批处理中的请求。 此属性是引用不同单个请求的 ID 的字符串数组。 例如,在以下请求中,客户端指定应按请求 1、请求 2、请求 4、请求 3 的顺序运行请求。

{
  "requests": [
    {
      "id": "1",
      "method": "GET",
      "url": "..."
    },
    {
      "id": "2",
      "dependsOn": [ "1" ],
      "method": "GET",
      "url": "..."
    },
    {
      "id": "4",
      "dependsOn": [ "2" ],
      "method": "GET",
      "url": "..."
    },
    {
      "id": "3",
      "dependsOn": [ "4" ],
      "method": "GET",
      "url": "..."
    }
  ]
}

如果单独请求失败,任何依赖此请求的请求都会失败,且状态代码为 424(依赖项失败)。

提示

批处理应是完全有序的或完全并行的。

使用批处理绕过 URL 长度限制

JSON 批处理的另一个用例是绕过 URL 长度限制。 如果 filter 子句很复杂,URL 长度可能会超过浏览器或其他 HTTP 客户端内置的限制。 可以使用 JSON 批处理作为运行这些请求的解决方法,因为冗长的 URL 只是请求有效负载的一部分。

批大小限制

  • JSON 批处理请求目前限定为 20 个单独请求。
  • 根据批处理请求的 API, 基础服务会施加自己的限制 ,这些限制会影响使用 Microsoft Graph 访问它们的应用程序。
  • 将针对适用的限制单独评估批处理中的请求,如果任何请求超出限制,则失败,状态 429为 。

有关详细信息,请参阅 限制和批处理。

已知问题

有关与批处理相关的当前限制列表,请参阅已知问题。