跳至主要内容

处理 Webhook 传递

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

简介

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

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

设置

为了在本地测试您的 Webhook,您可以使用 Webhook 代理 URL 将 Webhook 从 GitHub 转发到您的计算机或 Codespace。本文使用 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 订阅的事件类型,以及您在创建 Webhook 时 GitHub 发送的 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 sent the ping event”。您还可能会看到 Sinatra 自动打印的其他一些行。

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

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

JavaScript 示例

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

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

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

JavaScript 示例:安装依赖项

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

Shell
npm install express

JavaScript 示例:编写代码

创建一个包含以下内容的 JavaScript 文件。修改代码以处理您的 Webhook 订阅的事件类型,以及您创建 Webhook 时 GitHub 发送的 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. 在单独的终端窗口中,运行以下命令以在您的计算机或 Codespace 上启动本地服务器。将 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 的最佳实践”。

进一步阅读