{"meta":{"title":"处理 Webhook 交付","intro":"了解如何编写代码来侦听和响应 Webhook 交付。","product":"Webhook","breadcrumbs":[{"href":"/zh/webhooks","title":"Webhook"},{"href":"/zh/webhooks/using-webhooks","title":"使用网络钩子（Webhook）"},{"href":"/zh/webhooks/using-webhooks/handling-webhook-deliveries","title":"处理发货"}],"documentType":"article"},"body":"# 处理 Webhook 交付\n\n了解如何编写代码来侦听和响应 Webhook 交付。\n\n## 简介\n\n创建 Webhook 时，要指定 URL 并订阅事件类型。 当您的 Webhook 所订阅的事件发生时，GitHub 会向您指定的 URL 发送一个 HTTP 请求，其中包含该事件的相关数据。 如果服务器设置为侦听该 URL 处的 Webhook 交付，则可在收到 Webhook 交付时采取措施。\n\n本文旨在介绍如何编写代码让服务器侦听和响应 Webhook 交付。 你将使用你的计算机或 codespace 作为本地服务器来测试你的代码。\n\n## 安装\n\n为了在本地测试 Webhook，可以使用 Webhook 代理 URL 将来自 GitHub 的 Webhook 转发到你的计算机或 codespace。 本文使用 Smee.io 提供 Webhook 代理 URL 和转发 Webhook。\n\n### 获取 Webhook 代理 URL\n\n1. 在浏览器中，导航到  <https://smee.io/> 。\n2. 单击**启动新频道**。\n3. 复制“Webhook 代理 URL”下的完整 URL。 后续设置步骤会用到此 URL。\n\n### 转发 Webhook\n\n1. 如果尚未安装 [smee-client](https://www.npmjs.com/package/smee-client)，请在终端中运行以下命令：\n\n   ```shell copy\n   npm install --global smee-client\n   ```\n\n2. 若要通过 smee.io 接收转发的 Webhook，请在终端中运行以下命令。 将 `WEBHOOK_PROXY_URL` 替换为前面提到的 Webhook 代理 URL。\n\n   ```shell copy\n   smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000\n   ```\n\n   应会看到如下所示的输出，其中 `WEBHOOK_PROXY_URL` 是 Webhook 代理 URL：\n\n   ```shell copy\n   Forwarding WEBHOOK_PROXY_URL to http://127.0.0.1:3000/webhook\n   Connected WEBHOOK_PROXY_URL\n   ```\n\n   请注意，路径为 `/webhook`，端口为 `3000`。 这些值会在稍后编写代码来处理 Webhook 交付时用到。\n\n3. 在测试 Webhook 时保持此运行状态。 如果要停止转发 Webhook，请输入 <kbd>Ctrl</kbd>+<kbd>C</kbd>。\n\n### 创建 Webhook (网络钩子)\n\n1. 使用以下设置创建 Webhook。 有关详细信息，请参阅“[创建网络钩子](/zh/webhooks/using-webhooks/creating-webhooks)”。\n\n   * 对于 URL，请使用先前指定的 Webhook 代理 URL。\n   * 如果可以选择内容类型，请使用 JSON。\n\n## 编写代码来处理 Webhook 交付\n\n若要处理 Webhook 交付，需要编写具有以下功能的代码：\n\n* 初始化服务器来侦听对 Webhook URL 的请求\n* 从请求中读取 HTTP 头和正文\n* 执行所需操作来响应请求\n\n您可以使用任何能够在您的服务器上运行的编程语言。\n\n以下示例会在收到 Webhook 交付时打印消息。 但是，可以修改代码以执行其他操作，例如向 GitHub API 发出请求或发送 Slack 消息。\n\n* [Ruby 示例](#ruby-example)\n* [JavaScript 示例](#javascript-example)\n\n### Ruby 示例\n\n此示例使用 Ruby gem Sinatra 来定义路由并处理 HTTP 请求。 有关详细信息，请参阅 [Sinatra 自述文件](https://github.com/sinatra/sinatra#readme)。\n\n#### Ruby 示例：安装依赖关系\n\n若要使用此示例，必须在 Ruby 项目中安装 Sinatra gem。 例如，可以通过使用[Bundler](https://bundler.io/)来执行此操作：\n\n1. 如果尚未安装捆绑程序，请在终端中运行以下命令：\n\n   ```shell copy\n   gem install bundler\n   ```\n\n2. 如果应用还没有 Gemfile，请在终端中运行以下命令：\n\n   ```shell copy\n   bundle init\n   ```\n\n3. 如果应用还没有 Gemfile.lock，请在终端中运行以下命令：\n\n   ```shell copy\n   bundle install\n   ```\n\n4. 在终端中运行以下命令来安装 Sinatra gem：\n\n   ```shell copy\n   bundle add sinatra\n   ```\n\n#### Ruby 示例：编写代码\n\n创建具有以下内容的 Ruby 文件。 修改代码，以处理你的 Webhook 所订阅的事件类型，以及 `ping` 在你创建 Webhook 时发送的 GitHub 事件。 此示例处理 `issues` 和 `ping` 事件。\n\n```ruby copy annotate\n# These are the dependencies for this code. You installed the `sinatra` gem earlier. For more information, see [Ruby example: Install dependencies](#ruby-example-install-dependencies). The `json` library is a standard Ruby library, so you don't need to install it.\nrequire 'sinatra'\nrequire 'json'\n\n# The `/webhook` route matches the path that you specified for the smee.io forwarding. For more information, see [Forward webhooks](#forward-webhooks).\n#\n# Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.\npost '/webhook' do\n\n  # Respond to indicate that the delivery was successfully received.\n  # Your server should respond with a 2XX response within 10 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.\n  status 202\n\n  # Check the `X-GitHub-Event` header to learn what event type was sent.\n  # Sinatra changes `X-GitHub-Event` to `HTTP_X_GITHUB_EVENT`.\n  github_event = request.env['HTTP_X_GITHUB_EVENT']\n\n  # You should add logic to handle each event type that your webhook is subscribed to.\n  # For example, this code handles the `issues` and `ping` events.\n  #\n  # If any events have an `action` field, you should also add logic to handle each action that you are interested in.\n  # For example, this code handles the `opened` and `closed` actions for the `issue` event.\n  #\n  # For more information about the data that you can expect for each event type, see [AUTOTITLE](/webhooks/webhook-events-and-payloads).\n  if github_event == \"issues\"\n    data = JSON.parse(request.body.read)\n    action = data['action']\n    if action == \"opened\"\n      puts \"An issue was opened with this title: #{data['issue']['title']}\"\n    elsif action == \"closed\"\n      puts \"An issue was closed by #{data['issue']['user']['login']}\"\n    else\n      puts \"Unhandled action for the issue event: #{action}\"\n    end\n  elsif github_event == \"ping\"\n    puts \"GitHub sent the ping event\"\n  else\n    puts \"Unhandled event: #{github_event}\"\n  end\nend\n```\n\n#### Ruby 示例：测试代码\n\n若要测试 Webhook，可以使用计算机或 codespace 充当本地服务器。 如果在执行这些步骤时遇到问题，请参阅[疑难解答](#troubleshooting)。\n\n1. 确保正在转发 Webhook。 如果您不再转发 webhooks，请再次按[转发 webhooks](#forward-webhooks)中的步骤进行操作。\n\n2. 在单独的终端窗口中运行以下命令，在计算机上或 codespace 上启动本地服务器。 将 `FILE_PATH` 替换为存储前文代码的文件的路径。 请注意，`PORT=3000` 与在上一步中为 Webhook 转发指定的端口匹配。\n\n   ```shell copy\n   PORT=3000 ruby FILE_NAME\n   ```\n\n   应该会看到类似“Sinatra 已在 3000 上运行”的输出。\n\n3. 触发你的 Webhook。 例如，如果创建了订阅 `issues` 事件的存储库 Webhook，可以在存储库中提出问题。 您还可以重新发送先前的 Webhook 传送。 有关详细信息，请参阅“[重新传递 Webhook](/zh/webhooks/testing-and-troubleshooting-webhooks/redelivering-webhooks)”。\n\n4. 访问 smee.io 上的 Webhook 代理 URL。 应该会看到与已触发或已重新交付事件对应的事件。 这表示 GitHub 已成功将 Webhook 投递到你指定的载荷 URL。\n\n5. 在运行 `smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000` 的终端窗口中，应该会看到类似 `POST http://127.0.0.1:3000/webhook - 202` 的内容。 这表示 smee 已成功将 Webhook 转发到本地服务器。\n\n6. 在运行 `PORT=3000 ruby FILE_NAME` 的终端窗口中，应该会看到与已发送事件对应的消息。 例如，如果使用上述示例代码并重新传送 `ping` 事件，则应看到“GitHub 发送 ping 事件”。 还可以看到 Sinatra 自动打印的其他行。\n\n7. 在这两个终端窗口中，输入 <kbd>Ctrl</kbd>+<kbd>C</kbd> 停止本地服务器并停止侦听转发的 Webhook。\n\n现已在本地完成代码测试，可以根据情况进行更改，以便在生产环境中使用 Webhook。 有关详细信息，请参阅[后续步骤](#next-steps)。 如果在测试代码时遇到问题，请尝试“[疑难解答](#troubleshooting)”中的步骤。\n\n### JavaScript 示例\n\n此示例使用 Node.js 和 Express 库来定义路由并处理 HTTP 请求。 有关详细信息，请参阅 [expressjs.com](https://expressjs.com)。\n\n有关使用 GitHub'Octokit.js SDK 的示例，请参阅 [构建响应 Webhook 事件的GitHub应用](/zh/apps/creating-github-apps/writing-code-for-a-github-app/building-a-github-app-that-responds-to-webhook-events)。\n\n本示例要求计算机或 codespace 运行 Node.js 版本 12 或更高版本和 npm 版本 6.12.0 或更高版本。 有关详细信息，请参阅 [Node.js](https://nodejs.org)。\n\n#### JavaScript 示例：安装依赖关系\n\n若要使用此示例，必须在 Node.js 项目中安装 `express` 库。 例如：\n\n```shell copy\nnpm install express\n```\n\n#### JavaScript 示例：编写代码\n\n创建具有以下内容的 JavaScript 文件。 修改代码，以处理你的 Webhook 所订阅的事件类型，以及 `ping` 在你创建 Webhook 时发送的 GitHub 事件。 此示例处理 `issues` 和 `ping` 事件。\n\n```javascript copy annotate\n// You installed the `express` library earlier. For more information, see [JavaScript example: Install dependencies](#javascript-example-install-dependencies).\nconst express = require('express');\n\n// This initializes a new Express application.\nconst app = express();\n\n// This defines a POST route at the `/webhook` path. This path matches the path that you specified for the smee.io forwarding. For more information, see [Forward webhooks](#forward-webhooks).\n//\n// Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.\napp.post('/webhook', express.json({type: 'application/json'}), (request, response) => {\n\n  // Respond to indicate that the delivery was successfully received.\n  // Your server should respond with a 2XX response within 10 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.\n  response.status(202).send('Accepted');\n\n  // Check the `x-github-event` header to learn what event type was sent.\n  const githubEvent = request.headers['x-github-event'];\n\n  // You should add logic to handle each event type that your webhook is subscribed to.\n  // For example, this code handles the `issues` and `ping` events.\n  //\n  // If any events have an `action` field, you should also add logic to handle each action that you are interested in.\n  // For example, this code handles the `opened` and `closed` actions for the `issue` event.\n  //\n  // For more information about the data that you can expect for each event type, see [AUTOTITLE](/webhooks/webhook-events-and-payloads).\n  if (githubEvent === 'issues') {\n    const data = request.body;\n    const action = data.action;\n    if (action === 'opened') {\n      console.log(`An issue was opened with this title: ${data.issue.title}`);\n    } else if (action === 'closed') {\n      console.log(`An issue was closed by ${data.issue.user.login}`);\n    } else {\n      console.log(`Unhandled action for the issue event: ${action}`);\n    }\n  } else if (githubEvent === 'ping') {\n    console.log('GitHub sent the ping event');\n  } else {\n    console.log(`Unhandled event: ${githubEvent}`);\n  }\n});\n\n// This defines the port where your server should listen.\n// 3000 matches the port that you specified for webhook forwarding. For more information, see [Forward webhooks](#forward-webhooks).\n//\n// Once you deploy your code to a server, you should change this to match the port where your server is listening.\nconst port = 3000;\n\n// This starts the server and tells it to listen at the specified port.\napp.listen(port, () => {\n  console.log(`Server is running on port ${port}`);\n});\n```\n\n#### JavaScript 示例：测试代码\n\n若要测试 Webhook，可以使用计算机或 codespace 充当本地服务器。 如果在执行这些步骤时遇到问题，请参阅[疑难解答](#troubleshooting)。\n\n1. 确保正在转发 Webhook。 如果您不再转发 webhooks，请再次按[转发 webhooks](#forward-webhooks)中的步骤进行操作。\n\n2. 在单独的终端窗口中运行以下命令，在计算机上或 codespace 上启动本地服务器。 将 `FILE_PATH` 替换为存储前文代码的文件的路径。\n\n   ```shell copy\n   node FILE_NAME\n   ```\n\n   应会看到输出显示 `Server is running on port 3000`。\n\n3. 触发你的 Webhook。 例如，如果创建了订阅 `issues` 事件的存储库 Webhook，可以在存储库中提出问题。 您还可以重新发送先前的 Webhook 传送。 有关详细信息，请参阅“[重新传递 Webhook](/zh/webhooks/testing-and-troubleshooting-webhooks/redelivering-webhooks)”。\n\n4. 访问 smee.io 上的 Webhook 代理 URL。 应该会看到与已触发或已重新交付事件对应的事件。 这表示 GitHub 已成功将 Webhook 投递到你指定的载荷 URL。\n\n5. 在运行 `smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000` 的终端窗口中，应该会看到类似 `POST http://127.0.0.1:3000/webhook - 202` 的内容。 这表示 smee 已成功将 Webhook 转发到本地服务器。\n\n6. 在运行 `node FILE_NAME` 的终端窗口中，应该会看到与已发送事件对应的消息。 例如，如果使用上述示例代码并重新传送 `ping` 事件，则应看到“GitHub 发送 ping 事件”。\n\n7. 在这两个终端窗口中，输入 <kbd>Ctrl</kbd>+<kbd>C</kbd> 停止本地服务器并停止侦听转发的 Webhook。\n\n现已在本地完成代码测试，可以根据情况进行更改，以便在生产环境中使用 Webhook。 有关详细信息，请参阅[后续步骤](#next-steps)。 如果在测试代码时遇到问题，请尝试“[疑难解答](#troubleshooting)”中的步骤。\n\n## 故障排除\n\n如果未看到测试步骤中所述的预期结果，请尝试以下操作：\n\n* 确保 Webhook 使用的是 Webhook 代理 URL (Smee.io URL)。 有关 Webhook 代理 URL 的详细信息，请参阅[获取 Webhook 代理 URL](#get-a-webhook-proxy-url)。 有关 Webhook 设置的详细信息，请参阅“[创建网络钩子](/zh/webhooks/using-webhooks/creating-webhooks)”。\n* 如果可以选择要使用的内容类型，请确保 Webhook 使用 JSON 内容类型。 有关 Webhook 设置的详细信息，请参阅“[创建网络钩子](/zh/webhooks/using-webhooks/creating-webhooks)”。\n* 确保 smee 客户端和本地服务器都正在运行。 将在两个单独的终端窗口中运行这些进程。\n* 确保服务器正在侦听 smee.io 转发 Webhook 的同一端口。 本文中的所有示例都使用端口 3000。\n* 确保 smee.io 转发 Webhook 的路径匹配代码中定义的路由。 本文中的所有示例使用 `/webhooks` 路径。\n* 检查正在运行 smee 客户端和本地服务器的终端窗口中是否有错误消息。\n* 检查 GitHub 以确认是否触发了 Webhook 传递。 有关详细信息，请参阅“[查看 web 挂钩交付](/zh/webhooks/testing-and-troubleshooting-webhooks/viewing-webhook-deliveries)”。\n* 请在 smee.io 上检查您的 webhook 代理 URL。 应该会看到与已触发或已重新交付事件对应的事件。 这表示 GitHub 已成功将 Webhook 投递到你指定的载荷 URL。\n\n## 后续步骤\n\n本文演示了如何编写代码来处理 Webhook 交付。 它还演示了如何将计算机或 codespace 用作本地服务器来测试代码，以及如何通过 smee.io 将来自 GitHub 的 Webhook 传递转发到本地服务器。 测试代码后，可能需要修改代码并将代码部署到服务器。\n\n### 修改代码\n\n本文提供了在收到 Webhook 交付时打印消息的基本示例。 若要执行其他操作，可以对代码进行修改。 例如，可以修改代码，从而：\n\n* 向 GitHub API 发出请求\n* 在 Slack 上发送消息\n* 日志事件\n* 更新外部项目管理工具\n\n### 验证传递是否来自 GitHub\n\n在处理 Webhook 投递的代码中，在进一步处理该投递之前，应先验证该投递是否来自 GitHub。 有关详细信息，请参阅“[验证 Webhook 交付](/zh/webhooks/using-webhooks/validating-webhook-deliveries)”。\n\n### 将代码部署到服务器\n\n本文演示了如何在开发代码时使用计算机或 codespace 作为服务器。 代码可供生产使用后，应将应用部署到专用服务器。\n\n执行此操作时，可能需要更新代码来反映服务器正在侦听的主机和端口。\n\n### 更新 webhook 的链接地址\n\n设置好可接收来自 GitHub 的 webhook 流量的服务器后，请在 webhook 设置中更新该 URL。 可能需要更新代码处理的路径以匹配新 URL 的路由部分。 例如，若新的 Webhook URL 为 `https://example.com/github-webhooks`，则应将这些示例中的路由从 `/webhooks` 更改为 `/github-webhooks`。\n\n不应使用 Smee.io 在生产环境中转发 Webhook。\n\n### 遵循最佳做法\n\n应该遵循 Webhook 的最佳做法。 有关详细信息，请参阅“[使用 Webhook 的最佳做法](/zh/webhooks/using-webhooks/best-practices-for-using-webhooks)”。\n\n## 其他阅读材料\n\n* [构建响应 Webhook 事件的GitHub应用](/zh/apps/creating-github-apps/writing-code-for-a-github-app/building-a-github-app-that-responds-to-webhook-events)\n* [使用 Webhook 的最佳做法](/zh/webhooks/using-webhooks/best-practices-for-using-webhooks)"}