# 使用 REST API 的最佳做法

使用 GitHub's API 时，请遵循这些最佳做法。

## 避免轮询

应订阅 Webhook 事件，而不是通过轮询 API 来获取数据。 这有助于将集成保持在 API 速率限制内。 有关详细信息，请参阅“[Webhooks 文档](/zh/webhooks)”。

如果无法使用 Webhook，并且必须轮询 API，请尽可能高效地轮询以避免超出速率限制：

* 只需按实际需要的频率，按照固定计划进行轮询。 如果响应包含一个 `x-poll-interval` 标头，请至少等待那么多秒，然后再轮询同一终结点。
* 发出经过身份验证的条件请求，使未更改的数据不计入主要速率限制。 有关详细信息，请参阅 [“使用条件请求](#use-conditional-requests)”。
* 只请求所需的数据，并保持响应稳定，以便让更多轮询返回 `304 Not Modified`。 有关详细信息，请参阅 [“发出可缓存的请求](#make-requests-that-can-be-cached)”。

## 发出经身份验证的请求

经身份验证的请求的主要速率限制高于未经身份验证的请求。 为避免超出速率限制，应发出经过身份验证的请求。 有关详细信息，请参阅“[REST API 的速率限制](/zh/rest/using-the-rest-api/rate-limits-for-the-rest-api)”。

## 避免并发请求

为避免超出辅助速率限制，应采用串行方式发出请求，而不是并行发出请求。 为此，可以为请求实施队列系统。

## 在可变请求之间暂停

如果要发出大量的 `POST`、`PATCH`、`PUT` 或 `DELETE` 请求，则请求之间至少应间隔一秒钟。 这将帮助您避免次级速率限制。

## 恰当处理速率限制错误

如果收到速率限制错误，应当根据以下指导原则暂时停止发出请求：

* 如果有 `retry-after` 响应头，则应先等待数秒，然后再尝试请求。
* 如果 `x-ratelimit-remaining` 标头为 `0`，应在 `x-ratelimit-reset` 标头指定的时间之后再尝试发出另一个请求。 标头 `x-ratelimit-reset` 以 UTC 纪元秒为单位。
* 否则，请在重试之前等待至少一分钟。 如果您的请求因二级速率限制而持续失败，请在每次重试之间等待呈指数级增长的时间，并在达到特定重试次数后抛出一个错误。

如果在受到速率限制的情况下继续发出请求，可能会导致禁止集成。

## 跟随重定向

GitHub REST API 在适当情况下使用 HTTP 重定向。 应假定任何请求都可能会导致重定向。 收到 HTTP 重定向不代表出现错误，应遵循该重定向。

`301` 状态代码指示永久重定向。 应将请求重复到 `location` 标头指定的 URL。 此外，应更新代码以将此 URL 用于之后的请求。

`302` 或 `307` 状态代码指示临时重定向。 应将请求重复到 `location` 标头指定的 URL。 但是，不应更新代码以将此 URL 用于之后的请求。

可能会根据 HTTP 规范使用其他重定向状态代码。

## 请勿手动分析 URL

许多 API 终结点会在响应正文中返回字段的 URL 值。 不应尝试分析这些 URL 或预测之后 URL 的结构。 如果 GitHub 将来更改 URL 的结构，这可能会导致集成中断。 相反，应查找包含所需信息的字段。 例如，创建问题的终结点会返回一个 `html_url` 字段，其值类似 `https://github.com/octocat/Hello-World/issues/1347`，以及 `number` 字段，其值类似 `1347`。 如果需要知道问题的数量，请使用 `number` 字段，而不是分析 `html_url` 字段。

同样，不应尝试手动构造分页查询。 而是应使用链接标头来确定可以请求的结果页。 有关详细信息，请参阅“[在 REST API 中使用分页](/zh/rest/using-the-rest-api/using-pagination-in-the-rest-api)”。

## 使用条件请求

大多数终结点会返回 `etag` 标头，许多终结点会返回 `last-modified` 标头。 可以使用这些标头的值发出条件 `GET` 请求。 如果响应未更改，将收到 `304 Not Modified` 响应。 在正确使用 `304` 标头授权的情况下发出条件请求时，如果返回 `Authorization` 响应，则该请求不计入主速率限制。 这会使条件请求在轮询终结点时特别有用，因为每个 `304 Not Modified` 响应都很快且不使用速率限制。

在以下示例中，请将 `YOUR-TOKEN` 替换为您的访问令牌。

要使用 `etag` 发出条件请求：

1. 发送请求并保存响应中 `etag` 标头的值。

   ```shell
   curl --include --header "Authorization: Bearer YOUR-TOKEN" https://api.github.com/repos/octocat/Spoon-Knife/pulls
   ```

   响应包括标头 `etag` ：

   ```text
   HTTP/2 200
   etag: "644b5b0155e6404a9cc4bd9d8b1ae730"
   ```

2. 在下一次向同一 URL 发出的请求中，在 `if-none-match` 标头中发送已保存的值。

   ```shell
   curl --include --header "Authorization: Bearer YOUR-TOKEN" --header 'if-none-match: "644b5b0155e6404a9cc4bd9d8b1ae730"' https://api.github.com/repos/octocat/Spoon-Knife/pulls
   ```

   如果数据未更改，将收到响应 `304 Not Modified` ，该响应不计入主要速率限制：

   ```text
   HTTP/2 304
   ```

还可以使用 `last-modified` 标头。 例如，如果上一个请求返回 `last-modified` 标头，值为 `Wed, 25 Oct 2023 19:17:59 GMT`，则可以在之后的请求中使用 `if-modified-since` 标头：

```shell
curl --include --header "Authorization: Bearer YOUR-TOKEN" --header 'if-modified-since: Wed, 25 Oct 2023 19:17:59 GMT' https://api.github.com/repos/octocat/Spoon-Knife
```

除非特定终结点的文档另有说明，否则不支持对不安全方法（例如 `POST`、`PUT`、`PATCH` 和 `DELETE`）的条件请求。

## 发出可缓存的请求

条件请求仅在终结点返回 `304 Not Modified`时节省时间和速率限制。 当请求的表示形式自保存其`304`或`etag`值以来未更改时，终结点将返回`last-modified`;不相关的响应标头（如日期）仍可能有所不同。 若要在轮询时更有可能获得 `304` 响应，请使请求保持稳定且具体。

仅请求所需的数据。 较小的、更具体的响应更改频率较低，因此返回 `304 Not Modified` 的频率更高。 例如，若要检查一个分支的拉取请求，请按该分支筛选列表，而不是列出每个拉取请求并自行搜索结果。 将 `HEAD-OWNER` 替换为拥有头分支的帐户；对于来自派生仓库的拉取请求，该帐户是拥有该派生仓库的帐户。 用分支名称替换 `BRANCH-NAME`，如果该名称包含特殊字符（例如 `#` 或 `&`），请对其进行 URL 编码：

```shell
curl --include --header "Authorization: Bearer YOUR-TOKEN" "https://api.github.com/repos/octocat/Spoon-Knife/pulls?head=HEAD-OWNER:BRANCH-NAME"
```

如果浏览列表，请使用稳定的排序顺序。 每当项发生更改时，某些参数（例如 `sort=updated`）对列表重新排序。 当项移动到新位置时，其旧位置与新位置之间的项将转移到不同的页面上，以便已提取的页面可以返回新数据，而不是 `304 Not Modified`。 稳定的顺序（如默认值）会停止对现有项的更新重新排序，尽管添加或删除项仍可将条目转移到其他页面上。

每次轮询相同的数据时，都使用相同的参数。 不同的页面大小、页码或筛选器会产生包含不同 `etag` 的不同响应。

## 请勿忽略错误

不应忽略重复的 `4xx` 和 `5xx` 错误代码。 相反，应确保与 API 正确进行交互。 例如，如果某个终结点请求字符串，而你向其传递一个数值，则你将会收到验证错误。 同样，试图访问未经授权或不存在的终结点会导致 `4xx` 错误。

如果要轮询，并且资源重复返回 `404 Not Found` 响应，请不要在每次轮询时继续请求它。 首先，请确保 `404` 不是由身份验证或授权问题引起的。 对于某些私有资源，当你的凭据不授予访问权限时，GitHub 返回的是 `404 Not Found` 响应，而不是 `403 Forbidden` 响应，因此，`404` 并不总是意味着该资源不存在。 有关详细信息，请参阅“[REST API 故障排除](/zh/rest/using-the-rest-api/troubleshooting-the-rest-api#404-not-found-for-an-existing-resource)”。 确认凭据正确后，请等待更长时间，然后再次检查，或仅当有理由相信资源现在存在时，才再次检查。 重复请求缺少的资源会浪费速率限制，并可以触发辅助速率限制。

故意忽略重复的验证错误可能会导致您的应用程序因滥用而被暂停。

## 其他阅读材料

* [使用 Webhook 的最佳做法](/zh/webhooks/using-webhooks/best-practices-for-using-webhooks)
* [创建GitHub应用的最佳做法](/zh/apps/creating-github-apps/about-creating-github-apps/best-practices-for-creating-a-github-app)