跳至主要内容

处理 Webhook 传递

了解如何编写代码来监听和响应 Webhook 传递。

简介

创建 Webhook 时,您需要指定一个 URL 并订阅事件类型。当您的 Webhook 订阅的事件发生时,GitHub 会向您指定的 URL 发送包含有关该事件数据的 HTTP 请求。如果您的服务器已设置为在该 URL 处监听 Webhook 传递,则它可以在收到传递时采取行动。

本文介绍如何编写代码让您的服务器监听和响应 Webhook 传递。您将使用您的计算机或代码空间作为本地服务器来测试您的代码。

设置

为了在本地测试您的 Webhook,您可以使用 Webhook 代理 URL 将来自 GitHub 的 Webhook 转发到您的计算机或代码空间。本文使用 smee.io 提供 Webhook 代理 URL 并转发 Webhook。

获取 Webhook 代理 URL

  1. 在您的浏览器中,导航到 https://smee.io/.
  2. 点击启动新频道
  3. 复制“Webhook 代理 URL”下的完整 URL。您将在以下设置步骤中使用此 URL。

转发 Webhook

  1. 如果您尚未安装 smee-client,请在您的终端中运行以下命令

    Shell
    npm install --global smee-client
    
  2. 要接收来自 smee.io 的转发 Webhook,请在您的终端中运行以下命令。将 WEBHOOK_PROXY_URL 替换为您之前获得的 Webhook 代理 URL。

    Shell
    smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000
    

    您应该看到类似于以下输出,其中 WEBHOOK_PROXY_URL 是您的 Webhook 代理 URL

    Shell
    Forwarding WEBHOOK_PROXY_URL to http://127.0.0.1:3000/webhook
    Connected WEBHOOK_PROXY_URL
    

    请注意,路径为 /webhook,端口为 3000。您将在编写代码以处理 Webhook 传递时使用这些值。

  3. 在您测试 Webhook 时,请保持此程序运行。当您想要停止转发 Webhook 时,请按 Ctrl+C

创建 Webhook

  1. 创建具有以下设置的 Webhook。有关更多信息,请参阅“创建 Webhook”。

    • 对于 URL,请使用您之前获得的 Webhook 代理 URL。
    • 如果您有选择内容类型的选项,请使用 JSON。

编写代码以处理 Webhook 传递

为了处理 Webhook 传递,您需要编写以下代码:

  • 初始化您的服务器以侦听对您的 Webhook URL 的请求
  • 从请求中读取 HTTP 标头和正文
  • 根据请求采取所需的行动

您可以使用您可以在服务器上运行的任何编程语言。

以下示例在收到 Webhook 传递时打印一条消息。但是,您可以修改代码以采取其他操作,例如向 GitHub API 发出请求或发送 Slack 消息。

Ruby 示例

此示例使用 Ruby gem Sinatra 来定义路由和处理 HTTP 请求。有关更多信息,请参阅 Sinatra 自述文件

Ruby 示例:安装依赖项

要使用此示例,您必须在 Ruby 项目中安装 sinatra gem。例如,您可以使用 Bundler

  1. 如果您尚未安装 Bundler,请在您的终端中运行以下命令

    Shell
    gem install bundler
    
  2. 如果您尚未为您的应用程序创建 Gemfile,请在您的终端中运行以下命令

    Shell
    bundle init
    
  3. 如果您尚未为您的应用程序创建 Gemfile.lock,请在您的终端中运行以下命令

    Shell
    bundle install
    
  4. 通过在您的终端中运行以下命令来安装 Sinatra gem

    Shell
    bundle add sinatra
    

Ruby 示例:编写代码

创建一个包含以下内容的 Ruby 文件。修改代码以处理您的 Webhook 订阅的事件类型,以及 GitHub 在您创建 Webhook 时发送的 ping 事件。此示例处理 issuesping 事件。

Ruby
require 'sinatra'
require 'json'

这些是此代码的依赖项。您之前安装了 sinatra gem。有关更多信息,请参阅“Ruby 示例:安装依赖项”。json 库是标准的 Ruby 库,因此您无需安装它。

post '/webhook' do

/webhook 路由匹配您为 smee.io 转发指定的路径。有关更多信息,请参阅“转发 Webhook”。

将代码部署到服务器并更新 Webhook URL 后,应将此更改为与 Webhook URL 的路径部分匹配。

  status 202

响应以指示已成功接收传递。您的服务器应在收到 Webhook 传递后 10 秒内以 2XX 响应进行响应。如果您的服务器响应时间超过 10 秒,则 GitHub 会终止连接并将传递视为失败。

  github_event = request.env['HTTP_X_GITHUB_EVENT']

检查 X-GitHub-Event 标头以了解发送了哪种事件类型。Sinatra 将 X-GitHub-Event 更改为 HTTP_X_GITHUB_EVENT

  if github_event == "issues"
    data = JSON.parse(request.body.read)
    action = data['action']
    if action == "opened"
      puts "An issue was opened with this title: #{data['issue']['title']}"
    elsif action == "closed"
      puts "An issue was closed by #{data['issue']['user']['login']}"
    else
      puts "Unhandled action for the issue event: #{action}"
    end
  elsif github_event == "ping"
    puts "GitHub sent the ping event"
  else
    puts "Unhandled event: #{github_event}"
  end
end

您应该添加逻辑来处理 Webhook 订阅的每种事件类型。例如,此代码处理 issuesping 事件。

如果任何事件具有 action 字段,您还应该添加逻辑来处理您感兴趣的每个操作。例如,此代码处理 issue 事件的 openedclosed 操作。

有关您可以为每种事件类型预期的数据的更多信息,请参阅 "Webhook 事件和有效负载。"。

# 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.
require 'sinatra'
require 'json'

# The `/webhook` route matches the path that you specified for the smee.io forwarding. For more information, see "[Forward webhooks](#forward-webhooks)."
#
# 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.
post '/webhook' do

  # Respond to indicate that the delivery was successfully received.
  # 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.
  status 202

  # Check the `X-GitHub-Event` header to learn what event type was sent.
  # Sinatra changes `X-GitHub-Event` to `HTTP_X_GITHUB_EVENT`.
  github_event = request.env['HTTP_X_GITHUB_EVENT']

  # You should add logic to handle each event type that your webhook is subscribed to.
  # For example, this code handles the `issues` and `ping` events.
  #
  # If any events have an `action` field, you should also add logic to handle each action that you are interested in.
  # For example, this code handles the `opened` and `closed` actions for the `issue` event.
  #
  # For more information about the data that you can expect for each event type, see "[AUTOTITLE](/webhooks/webhook-events-and-payloads)."
  if github_event == "issues"
    data = JSON.parse(request.body.read)
    action = data['action']
    if action == "opened"
      puts "An issue was opened with this title: #{data['issue']['title']}"
    elsif action == "closed"
      puts "An issue was closed by #{data['issue']['user']['login']}"
    else
      puts "Unhandled action for the issue event: #{action}"
    end
  elsif github_event == "ping"
    puts "GitHub sent the ping event"
  else
    puts "Unhandled event: #{github_event}"
  end
end

Ruby 示例:测试代码

要测试您的 Webhook,您可以使用您的计算机或 Codespace 充当本地服务器。如果您在执行这些步骤时遇到问题,请参阅 故障排除

  1. 确保您正在转发 Webhook。如果您不再转发 Webhook,请再次按照 转发 Webhook 中的步骤操作。

  2. 在单独的终端窗口中,运行以下命令以在您的计算机或 Codespace 上启动本地服务器。将 FILE_PATH 替换为存储上一节代码的文件的路径。请注意,PORT=3000 与您在上一步中为 Webhook 转发指定的端口匹配。

    Shell
    PORT=3000 ruby FILE_NAME
    

    您应该看到类似于“Sinatra has taken the stage on 3000”的输出。

  3. 触发您的 Webhook。例如,如果您创建了一个订阅 issues 事件的存储库 Webhook,请在您的存储库中打开一个问题。您也可以重新传递以前的 Webhook 传递。有关更多信息,请参阅 "重新传递 Webhook。"。

  4. 导航到 smee.io 上的 Webhook 代理 URL。您应该看到一个与您触发或重新传递的事件相对应的事件。这表明 GitHub 已成功将 Webhook 传递发送到您指定的有效负载 URL。

  5. 在您运行 smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000 的终端窗口中,您应该看到类似于 POST http://127.0.0.1:3000/webhook - 202 的内容。这表明 smee 已成功将您的 Webhook 转发到您的本地服务器。

  6. 在您运行 PORT=3000 ruby FILE_NAME 的终端窗口中,您应该看到一条与发送的事件相对应的消息。例如,如果您使用上面的示例代码并重新发送了 ping 事件,您应该看到“GitHub 发送了 ping 事件”。您可能还会看到 Sinatra 自动打印的一些其他行。

  7. 在两个终端窗口中,输入 Ctrl+C 以停止您的本地服务器并停止监听转发的 Webhook。

现在您已经在本地测试了代码,您可以进行更改以在生产环境中使用您的 Webhook。有关更多信息,请参阅“下一步”。如果您在测试代码时遇到问题,请尝试“故障排除”中的步骤。

JavaScript 示例

此示例使用 Node.js 和 Express 库来定义路由和处理 HTTP 请求。有关更多信息,请参阅“expressjs.com”。

有关使用 GitHub 的 Octokit.js SDK 的示例,请参阅“构建响应 Webhook 事件的 GitHub 应用程序”。

此示例要求您的计算机或代码空间运行 Node.js 版本 12 或更高版本以及 npm 版本 6.12.0 或更高版本。有关更多信息,请参阅 Node.js

JavaScript 示例:安装依赖项

要使用此示例,您必须在 Node.js 项目中安装 express 库。例如

Shell
npm install express

JavaScript 示例:编写代码

创建一个包含以下内容的 JavaScript 文件。修改代码以处理您的 Webhook 订阅的事件类型以及 GitHub 在您创建 Webhook 时发送的 ping 事件。此示例处理 issuesping 事件。

JavaScript
const express = require('express');

您之前安装了 express 库。有关更多信息,请参阅“JavaScript 示例:安装依赖项”。

const app = express();

这将初始化一个新的 Express 应用程序。

app.post('/webhook', express.json({type: 'application/json'}), (request, response) => {

这在 /webhook 路径上定义了一个 POST 路由。此路径与您为 smee.io 转发指定的路径匹配。有关更多信息,请参阅“转发 Webhook”。

将代码部署到服务器并更新 Webhook URL 后,应将此更改为与 Webhook URL 的路径部分匹配。

  response.status(202).send('Accepted');

响应以指示已成功接收传递。您的服务器应在收到 Webhook 传递后 10 秒内以 2XX 响应进行响应。如果您的服务器响应时间超过 10 秒,则 GitHub 会终止连接并将传递视为失败。

  const githubEvent = request.headers['x-github-event'];

检查 x-github-event 标头以了解发送了哪种事件类型。

  if (githubEvent === 'issues') {
    const data = request.body;
    const action = data.action;
    if (action === 'opened') {
      console.log(`An issue was opened with this title: ${data.issue.title}`);
    } else if (action === 'closed') {
      console.log(`An issue was closed by ${data.issue.user.login}`);
    } else {
      console.log(`Unhandled action for the issue event: ${action}`);
    }
  } else if (githubEvent === 'ping') {
    console.log('GitHub sent the ping event');
  } else {
    console.log(`Unhandled event: ${githubEvent}`);
  }
});

您应该添加逻辑来处理 Webhook 订阅的每种事件类型。例如,此代码处理 issuesping 事件。

如果任何事件具有 action 字段,您还应该添加逻辑来处理您感兴趣的每个操作。例如,此代码处理 issue 事件的 openedclosed 操作。

有关您可以为每种事件类型预期的数据的更多信息,请参阅 "Webhook 事件和有效负载。"。

const port = 3000;

这定义了服务器应该监听的端口。3000 与您为 Webhook 转发指定的端口匹配。有关更多信息,请参阅“转发 Webhook”。

将代码部署到服务器后,您应该将其更改为与服务器监听的端口匹配。

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

这将启动服务器并告诉它监听指定的端口。

// You installed the `express` library earlier. For more information, see "[JavaScript example: Install dependencies](#javascript-example-install-dependencies)."
const express = require('express');

// This initializes a new Express application.
const app = express();

// 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)."
//
// 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.
app.post('/webhook', express.json({type: 'application/json'}), (request, response) => {

  // Respond to indicate that the delivery was successfully received.
  // 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.
  response.status(202).send('Accepted');

  // Check the `x-github-event` header to learn what event type was sent.
  const githubEvent = request.headers['x-github-event'];

  // You should add logic to handle each event type that your webhook is subscribed to.
  // For example, this code handles the `issues` and `ping` events.
  //
  // If any events have an `action` field, you should also add logic to handle each action that you are interested in.
  // For example, this code handles the `opened` and `closed` actions for the `issue` event.
  //
  // For more information about the data that you can expect for each event type, see "[AUTOTITLE](/webhooks/webhook-events-and-payloads)."
  if (githubEvent === 'issues') {
    const data = request.body;
    const action = data.action;
    if (action === 'opened') {
      console.log(`An issue was opened with this title: ${data.issue.title}`);
    } else if (action === 'closed') {
      console.log(`An issue was closed by ${data.issue.user.login}`);
    } else {
      console.log(`Unhandled action for the issue event: ${action}`);
    }
  } else if (githubEvent === 'ping') {
    console.log('GitHub sent the ping event');
  } else {
    console.log(`Unhandled event: ${githubEvent}`);
  }
});

// This defines the port where your server should listen.
// 3000 matches the port that you specified for webhook forwarding. For more information, see "[Forward webhooks](#forward-webhooks)."
//
// Once you deploy your code to a server, you should change this to match the port where your server is listening.
const port = 3000;

// This starts the server and tells it to listen at the specified port.
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

JavaScript 示例:测试代码

要测试您的 Webhook,您可以使用您的计算机或 Codespace 充当本地服务器。如果您在执行这些步骤时遇到问题,请参阅 故障排除

  1. 确保您正在转发 Webhook。如果您不再转发 Webhook,请再次按照 转发 Webhook 中的步骤操作。

  2. 在单独的终端窗口中,运行以下命令以在您的计算机或代码空间上启动本地服务器。将 FILE_PATH 替换为存储您之前部分代码的文件的路径。

    Shell
    node FILE_NAME
    

    您应该看到输出显示 服务器正在端口 3000 上运行

  3. 触发您的 Webhook。例如,如果您创建了一个订阅 issues 事件的存储库 Webhook,请在您的存储库中打开一个问题。您也可以重新传递以前的 Webhook 传递。有关更多信息,请参阅 "重新传递 Webhook。"。

  4. 导航到 smee.io 上的 Webhook 代理 URL。您应该看到一个与您触发或重新传递的事件相对应的事件。这表明 GitHub 已成功将 Webhook 传递发送到您指定的有效负载 URL。

  5. 在您运行 smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000 的终端窗口中,您应该看到类似于 POST http://127.0.0.1:3000/webhook - 202 的内容。这表明 smee 已成功将您的 Webhook 转发到您的本地服务器。

  6. 在您运行 node FILE_NAME 的终端窗口中,您应该看到一条与发送的事件相对应的消息。例如,如果您使用上面的示例代码并重新发送了 ping 事件,您应该看到“GitHub 发送了 ping 事件”。

  7. 在两个终端窗口中,输入 Ctrl+C 以停止您的本地服务器并停止监听转发的 Webhook。

现在您已经在本地测试了代码,您可以进行更改以在生产环境中使用您的 Webhook。有关更多信息,请参阅“下一步”。如果您在测试代码时遇到问题,请尝试“故障排除”中的步骤。

故障排除

如果您没有看到测试步骤中描述的预期结果,请尝试以下操作

  • 确保您的 Webhook 使用您的 Webhook 代理 URL(Smee.io URL)。有关您的 Webhook 代理 URL 的更多信息,请参阅“获取 Webhook 代理 URL”。有关您的 Webhook 设置的更多信息,请参阅“创建 Webhook”。
  • 确保您的 Webhook 使用 JSON 内容类型,如果您可以选择使用哪种内容类型。有关您的 Webhook 设置的更多信息,请参阅“创建 Webhook”。
  • 确保 smee 客户端和您的本地服务器都在运行。您将在两个单独的终端窗口中运行这些进程。
  • 确保您的服务器正在监听 smee.io 转发 Webhook 的相同端口。本文中的所有示例都使用端口 3000。
  • 确保 smee.io 转发 Webhook 的路径与您代码中定义的路由匹配。本文中的所有示例都使用 /webhooks 路径。
  • 检查您运行 smee 客户端和本地服务器的终端窗口中的错误消息。
  • 检查 GitHub 以验证是否触发了 Webhook 传递。有关更多信息,请参阅“查看 Webhook 传递”。
  • 检查您在 smee.io 上的 Webhook 代理 URL。您应该看到一个与您触发或重新发送的事件相对应的事件。这表明 GitHub 已成功将 Webhook 传递发送到您指定的有效负载 URL。

下一步

本文演示了如何编写代码来处理 webhook 传递。它还演示了如何使用您的计算机或 codespace 作为本地服务器以及通过 smee.io 将 webhook 传递从 GitHub 转发到您的本地服务器来测试您的代码。完成代码测试后,您可能需要修改代码并将代码部署到服务器。

修改代码

本文提供了一些基本示例,这些示例在收到 webhook 传递时会打印一条消息。您可能希望修改代码以执行其他操作。例如,您可以修改代码以

  • 向 GitHub API 发出请求
  • 在 Slack 上发送消息
  • 记录事件
  • 更新外部项目管理工具

验证传递是否来自 GitHub

在处理 webhook 传递的代码中,您应该在进一步处理传递之前验证传递是否来自 GitHub。有关更多信息,请参阅“验证 webhook 传递”。

将代码部署到服务器

本文演示了如何在开发代码时使用您的计算机或 codespace 作为服务器。代码准备好投入生产使用后,您应该将代码部署到专用服务器。

这样做时,您可能需要更新代码以反映服务器正在监听的主机和端口。

更新 webhook URL

设置好接收来自 GitHub 的 webhook 流量的服务器后,请更新 webhook 设置中的 URL。您可能需要更新代码处理的路由以匹配新 URL 的路径部分。例如,如果您的新 webhook URL 是 https://example.com/github-webhooks,则应将这些示例中的路由从 /webhooks 更改为 /github-webhooks

您不应该在生产环境中使用 smee.io 转发 webhook。

遵循最佳实践

您应该努力遵循 webhook 的最佳实践。有关更多信息,请参阅“使用 webhook 的最佳实践”。

进一步阅读