简介
创建 Webhook 时,您需要指定一个 URL 并订阅事件类型。当您的 Webhook 订阅的事件发生时,GitHub 会向您指定的 URL 发送包含有关该事件数据的 HTTP 请求。如果您的服务器已设置为在该 URL 处监听 Webhook 传递,则它可以在收到传递时采取行动。
本文介绍如何编写代码让您的服务器监听和响应 Webhook 传递。您将使用您的计算机或代码空间作为本地服务器来测试您的代码。
设置
为了在本地测试您的 Webhook,您可以使用 Webhook 代理 URL 将来自 GitHub 的 Webhook 转发到您的计算机或代码空间。本文使用 smee.io 提供 Webhook 代理 URL 并转发 Webhook。
获取 Webhook 代理 URL
- 在您的浏览器中,导航到 https://smee.io/.
- 点击启动新频道。
- 复制“Webhook 代理 URL”下的完整 URL。您将在以下设置步骤中使用此 URL。
转发 Webhook
-
如果您尚未安装 smee-client,请在您的终端中运行以下命令
Shell npm install --global smee-client
npm install --global smee-client
-
要接收来自 smee.io 的转发 Webhook,请在您的终端中运行以下命令。将
WEBHOOK_PROXY_URL
替换为您之前获得的 Webhook 代理 URL。Shell smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000
smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000
您应该看到类似于以下输出,其中
WEBHOOK_PROXY_URL
是您的 Webhook 代理 URLShell Forwarding WEBHOOK_PROXY_URL to http://127.0.0.1:3000/webhook Connected WEBHOOK_PROXY_URL
Forwarding WEBHOOK_PROXY_URL to http://127.0.0.1:3000/webhook Connected WEBHOOK_PROXY_URL
请注意,路径为
/webhook
,端口为3000
。您将在编写代码以处理 Webhook 传递时使用这些值。 -
在您测试 Webhook 时,请保持此程序运行。当您想要停止转发 Webhook 时,请按 Ctrl+C 。
创建 Webhook
-
创建具有以下设置的 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
-
如果您尚未安装 Bundler,请在您的终端中运行以下命令
Shell gem install bundler
gem install bundler
-
如果您尚未为您的应用程序创建 Gemfile,请在您的终端中运行以下命令
Shell bundle init
bundle init
-
如果您尚未为您的应用程序创建 Gemfile.lock,请在您的终端中运行以下命令
Shell bundle install
bundle install
-
通过在您的终端中运行以下命令来安装 Sinatra gem
Shell bundle add sinatra
bundle add sinatra
Ruby 示例:编写代码
创建一个包含以下内容的 Ruby 文件。修改代码以处理您的 Webhook 订阅的事件类型,以及 GitHub 在您创建 Webhook 时发送的 ping
事件。此示例处理 issues
和 ping
事件。
# 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
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 订阅的每种事件类型。例如,此代码处理 issues
和 ping
事件。
如果任何事件具有 action
字段,您还应该添加逻辑来处理您感兴趣的每个操作。例如,此代码处理 issue
事件的 opened
和 closed
操作。
有关您可以为每种事件类型预期的数据的更多信息,请参阅 "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 充当本地服务器。如果您在执行这些步骤时遇到问题,请参阅 故障排除。
-
确保您正在转发 Webhook。如果您不再转发 Webhook,请再次按照 转发 Webhook 中的步骤操作。
-
在单独的终端窗口中,运行以下命令以在您的计算机或 Codespace 上启动本地服务器。将
FILE_PATH
替换为存储上一节代码的文件的路径。请注意,PORT=3000
与您在上一步中为 Webhook 转发指定的端口匹配。Shell PORT=3000 ruby FILE_NAME
PORT=3000 ruby FILE_NAME
您应该看到类似于“Sinatra has taken the stage on 3000”的输出。
-
触发您的 Webhook。例如,如果您创建了一个订阅
issues
事件的存储库 Webhook,请在您的存储库中打开一个问题。您也可以重新传递以前的 Webhook 传递。有关更多信息,请参阅 "重新传递 Webhook。"。 -
导航到 smee.io 上的 Webhook 代理 URL。您应该看到一个与您触发或重新传递的事件相对应的事件。这表明 GitHub 已成功将 Webhook 传递发送到您指定的有效负载 URL。
-
在您运行
smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000
的终端窗口中,您应该看到类似于POST http://127.0.0.1:3000/webhook - 202
的内容。这表明 smee 已成功将您的 Webhook 转发到您的本地服务器。 -
在您运行
PORT=3000 ruby FILE_NAME
的终端窗口中,您应该看到一条与发送的事件相对应的消息。例如,如果您使用上面的示例代码并重新发送了ping
事件,您应该看到“GitHub 发送了 ping 事件”。您可能还会看到 Sinatra 自动打印的一些其他行。 -
在两个终端窗口中,输入 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
库。例如
npm install express
npm install express
JavaScript 示例:编写代码
创建一个包含以下内容的 JavaScript 文件。修改代码以处理您的 Webhook 订阅的事件类型以及 GitHub 在您创建 Webhook 时发送的 ping
事件。此示例处理 issues
和 ping
事件。
// 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}`); });
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 订阅的每种事件类型。例如,此代码处理 issues
和 ping
事件。
如果任何事件具有 action
字段,您还应该添加逻辑来处理您感兴趣的每个操作。例如,此代码处理 issue
事件的 opened
和 closed
操作。
有关您可以为每种事件类型预期的数据的更多信息,请参阅 "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 充当本地服务器。如果您在执行这些步骤时遇到问题,请参阅 故障排除。
-
确保您正在转发 Webhook。如果您不再转发 Webhook,请再次按照 转发 Webhook 中的步骤操作。
-
在单独的终端窗口中,运行以下命令以在您的计算机或代码空间上启动本地服务器。将
FILE_PATH
替换为存储您之前部分代码的文件的路径。Shell node FILE_NAME
node FILE_NAME
您应该看到输出显示
服务器正在端口 3000 上运行
。 -
触发您的 Webhook。例如,如果您创建了一个订阅
issues
事件的存储库 Webhook,请在您的存储库中打开一个问题。您也可以重新传递以前的 Webhook 传递。有关更多信息,请参阅 "重新传递 Webhook。"。 -
导航到 smee.io 上的 Webhook 代理 URL。您应该看到一个与您触发或重新传递的事件相对应的事件。这表明 GitHub 已成功将 Webhook 传递发送到您指定的有效负载 URL。
-
在您运行
smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000
的终端窗口中,您应该看到类似于POST http://127.0.0.1:3000/webhook - 202
的内容。这表明 smee 已成功将您的 Webhook 转发到您的本地服务器。 -
在您运行
node FILE_NAME
的终端窗口中,您应该看到一条与发送的事件相对应的消息。例如,如果您使用上面的示例代码并重新发送了ping
事件,您应该看到“GitHub 发送了 ping 事件”。 -
在两个终端窗口中,输入 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 的最佳实践”。