{"meta":{"title":"在 REST API 中使用分页","intro":"了解如何从 REST API 浏览分页响应。","product":"REST API","breadcrumbs":[{"href":"/zh/rest","title":"REST API"},{"href":"/zh/rest/using-the-rest-api","title":"使用 REST API"},{"href":"/zh/rest/using-the-rest-api/using-pagination-in-the-rest-api","title":"分页"}],"documentType":"article"},"body":"# 在 REST API 中使用分页\n\n了解如何从 REST API 浏览分页响应。\n\n## 关于分页\n\n当 REST API 的响应包含许多结果时， GitHub 将分页结果并返回结果的子集。 例如，`GET /repos/octocat/Spoon-Knife/issues` 将仅返回 `octocat/Spoon-Knife` 存储库中的 30 个问题，即使存储库包含 1600 多个未解决的问题。 这使得服务器和用户的响应更易于处理。\n\n可以使用响应中的 `link` 标头来请求其他数据页。 如果端点支持 `per_page` 查询参数，你可以控制在页面上返回的结果数。\n\n本文演示如何在分页响应中请求其他结果页，如何更改每页返回的结果数，以及如何编写脚本来获取多页结果。\n\n## 使用 `link` 标头\n\n如果响应已分页，则响应头将包含 `link` 标头。 如果端点不支持分页或所有结果都显示在一页中，将省略 `link` 标头。\n\n`link` 标头包含可用于提取其他结果页的 URL。 例如，结果的上一页、下一页、第一页和最后一页。\n\n若要查看特定终结点的响应标头，可以使用 curl、GitHub CLI 或用于发出请求的库。 使用库发出请求时，若要查看响应头，请按照该库的文档进行操作。 若要查看响应标头，请在使用 curl 或 GitHub CLI 时随请求传递 `--include` 标志。 例如：\n\n```shell\ncurl --include --request GET \\\n--url \"https://api.github.com/repos/octocat/Spoon-Knife/issues\" \\\n--header \"Accept: application/vnd.github+json\"\n```\n\n如果响应已分页，`link` 标头将如下所示：\n\n```http\nlink: <https://api.github.com/repositories/1300192/issues?page=2>; rel=\"prev\", <https://api.github.com/repositories/1300192/issues?page=4>; rel=\"next\", <https://api.github.com/repositories/1300192/issues?page=515>; rel=\"last\", <https://api.github.com/repositories/1300192/issues?page=1>; rel=\"first\"\n```\n\n`link` 标头提供上一页、下一页、第一页和最后一页结果的 URL：\n\n* 上一页的 URL 后跟 `rel=\"prev\"`。\n* 下一页的 URL 后跟 `rel=\"next\"`。\n* 最后一页的 URL 紧跟着 `rel=\"last\"`。\n* 第一页的 URL 后跟 `rel=\"first\"`。\n\n在某些情况下，其中只有部分链接可用。 例如，如果位于结果的第一页，则不会包含指向上一页的链接；如果无法计算，则不会包含指向最后一页的链接。\n\n可以使用 `link` 标头中的 URL 请求另一页的结果。 例如，根据上一个示例请求最后一页的结果：\n\n```shell\ncurl --include --request GET \\\n--url \"https://api.github.com/repositories/1300192/issues?page=515\" \\\n--header \"Accept: application/vnd.github+json\"\n```\n\n`link` 标头中的 URL 使用查询参数来指示要返回的结果页。\n`link` URL 中的查询参数可能因端点而异，但每个分页端点都将使用 `page`、`before`/`after` 或 `since` 查询参数。 （某些端点将 `since` 参数用于非分页内容。）在所有情况下，都可以使用 `link` 接标头中的 URL 来提取其他结果页面。 有关查询参数的详细信息，请参阅 [REST API 入门](/zh/rest/using-the-rest-api/getting-started-with-the-rest-api#query-parameters)。\n\n## 更改每页显示的项数\n\n如果终结点支持 `per_page` 查询参数，则可以控制在页面上返回的结果数。 有关查询参数的详细信息，请参阅 [REST API 入门](/zh/rest/using-the-rest-api/getting-started-with-the-rest-api#query-parameters)。\n\n对于大多数终结点，最大值为 `per_page``100`. 如果指定的值大于最大值， GitHub 则不返回错误。 相反，该值会自动减少到最大值，并且响应包含的次数不超过每页的最大结果数。 由于请求仍然成功，因此可能会收到的结果少于预期结果，且没有任何指示 `per_page` 该值已减少。 若要确认终结点的默认值和最大值 `per_page` ，请参阅该终结点的参考文档。\n\n例如，此请求使用 `per_page` 查询参数，每页返回两个项：\n\n```shell\ncurl --include --request GET \\\n--url \"https://api.github.com/repos/octocat/Spoon-Knife/issues?per_page=2\" \\\n--header \"Accept: application/vnd.github+json\"\n```\n\n`per_page` 参数将自动包含在 `link` 标头中。 例如：\n\n```http\nlink: <https://api.github.com/repositories/1300192/issues?per_page=2&page=2>; rel=\"next\", <https://api.github.com/repositories/1300192/issues?per_page=2&page=7715>; rel=\"last\"\n```\n\n## 使用分页编写脚本\n\n可以编写脚本来提取多页结果，而不是手动复制 `link` 标头中的 URL。\n\n以下示例使用 JavaScript 和 GitHub's Octokit.js 库。 有关 Octokit.js 的详细信息，请参阅 [REST API 入门](/zh/rest/using-the-rest-api/getting-started-with-the-rest-api?tool=javascript) 和 [Octokit.js README](https://github.com/octokit/octokit.js/#readme)。\n\n### 使用 Octokit.js 分页方法的示例\n\n若要使用 Octokit.js 提取分页结果，可以使用 `octokit.paginate()`。\n`octokit.paginate()` 将提取下一个结果页到最后一页的结果，然后将所有结果作为单个数组返回。 一些终结点将分页结果作为对象中的数组返回，而不是以数组的形式返回分页结果。\n`octokit.paginate()` 始终返回项的数组，即使原始结果为一个对象。\n\n例如，此脚本从 `octocat/Spoon-Knife` 存储库获取所有问题。 虽然它一次请求 100 个问题，但函数在到达最后一页数据之前不会返回。\n\n```javascript copy\nimport { Octokit } from \"octokit\";\n\nconst octokit = new Octokit({ );\n\nconst data = await octokit.paginate(\"GET /repos/{owner}/{repo}/issues\", {\n  owner: \"octocat\",\n  repo: \"Spoon-Knife\",\n  per_page: 100,\n  headers: {\n    \"X-GitHub-Api-Version\": \"2026-03-10\",\n  },\n});\n\nconsole.log(data)\n```\n\n可以将可选的 map 函数传递给 `octokit.paginate()`，从而在到达最后一页之前结束分页，或者通过仅保留部分响应来减少内存使用量。 还可以使用 `octokit.paginate.iterator()` 一次循环访问单个页面，而不是请求每个页面。 有关详细信息，请参阅 [Octokit.js 文档](https://github.com/octokit/octokit.js#pagination)。\n\n### 创建分页方法的示例\n\n如果使用的其他语言或库没有分页方法，则可以生成自己的分页方法。 此示例仍使用 Octokit.js 库发出请求，但不依赖于 `octokit.paginate()`。\n\n`getPaginatedData` 函数使用 `octokit.request()` 向终结点发出请求。 响应中的数据由 `parseData` 处理，它处理以下两种情况：不返回数据或返回的数据是对象而不是数组。 然后，处理后的数据将追加到包含目前为止收集的所有分页数据的列表中。 如果响应包含 `link` 标头，并且 `link` 标头包含下一页的链接，则该函数使用 RegEx 模式 (`nextPattern`) 获取下一页的 URL。 然后，函数将重复上述步骤，现在使用此新 URL。\n`link` 标头不再包含指向下一页的链接后，将返回所有结果。\n\n```javascript copy\nimport { Octokit } from \"octokit\";\n\nconst octokit = new Octokit({ );\n\nasync function getPaginatedData(url) {\n  const nextPattern = /(?<=<)([\\S]*)(?=>; rel=\"next\")/i;\n  let pagesRemaining = true;\n  let data = [];\n\n  while (pagesRemaining) {\n    const response = await octokit.request(`GET ${url}`, {\n      per_page: 100,\n      headers: {\n        \"X-GitHub-Api-Version\":\n          \"2026-03-10\",\n      },\n    });\n\n    const parsedData = parseData(response.data)\n    data = [...data, ...parsedData];\n\n    const linkHeader = response.headers.link;\n\n    pagesRemaining = linkHeader && linkHeader.includes(`rel=\\\"next\\\"`);\n\n    if (pagesRemaining) {\n      url = linkHeader.match(nextPattern)[0];\n    }\n  }\n\n  return data;\n}\n\nfunction parseData(data) {\n  // If the data is an array, return that\n    if (Array.isArray(data)) {\n      return data\n    }\n\n  // Some endpoints respond with 204 No Content instead of empty array\n  //   when there is no data. In that case, return an empty array.\n  if (!data) {\n    return []\n  }\n\n  // Otherwise, the array of items that we want is in an object\n  // Delete keys that don't include the array of items\n  delete data.incomplete_results;\n  delete data.repository_selection;\n  delete data.total_count;\n  // Pull out the array of items\n  const namespaceKey = Object.keys(data)[0];\n  data = data[namespaceKey];\n\n  return data;\n}\n\nconst data = await getPaginatedData(\"/repos/octocat/Spoon-Knife/issues\");\n\nconsole.log(data);\n```"}