软件应用由相互连接的系统组成--每个系统都为满足商业需求的共同目标提供专门服务。与任何网络一样,有效的数据交换机制是其功能、有效性和响应性的关键。
在过去,数据交换是通过轮询请求进行的。每隔一段时间,系统就会发出请求,以获得最新的信息或找出是否有更新的信息需要处理。这种技术被证明是低效的,因为大多数请求被退回时没有新的信息可供操作。如果有新的东西需要处理,信息很可能是陈旧的,使得应用程序无法实时响应。
这导致了一种新的通信形式:webhooks。如果一个预先约定的事件发生,就会发出请求,通知需要该信息的系统,使该系统能够立即作出反应。使用webhooks的好处是降低了系统的开销,因为所发出的请求较少。它还可以确保数据的实时交换。CircleCI提供的webhooks允许您实时接收有关您的管道的信息。这可以帮助你避免轮询API或手动检查CircleCI网络应用程序以获得你需要的数据。
在本教程中,我将带领你建立一个Laravel API,它将被用作CircleCI管道的webhook,我们也将创建。
前提条件
要从本教程中获得最大的收获,你需要一些东西。
- 对PHP和Laravel有一个基本的了解
- PHP 7.4或更高版本,最好是PHP 8
- Git
- 合成器
- Laravel的安装程序
我们的教程是与平台无关的,但使用CircleCI作为一个例子。如果你没有CircleCI账户,请**在这里注册一个免费账户。**
CircleCI webhooks的使用案例
如果您仍然想知道CircleCI webhooks如何使用,无论您选择何种编程语言,这里有一些用例。
- 聚集来自单个或多个项目的事件,并发送数据到一个通信渠道,如Slack
- 当工作流程/工作被取消或完成时,自动向开发团队发送通知。
- 通过一个自定义的仪表板可视化和分析工作流程/工作事件。
- 将有价值的数据发送到数据记录和事件管理工具。
开始工作
创建一个新的项目来开始。
laravel new circle_ci_webhook_api
cd circle_ci_webhook_api
现在,为了设置webhook,你需要创建一个基本的CircleCI配置,并为CI管道设置一个GitHub仓库。在项目的根目录下,创建一个名为.circleci 的新文件夹。在.circleci 文件夹中,创建一个名为config.yml 的新文件。把这个文件添加到文件中。
version: 2
jobs:
build:
docker:
- image: cimg/php:8.0-browsers
steps:
- checkout
- run:
name: "Prepare Environment"
command: |
sudo apt update
# Download and cache dependencies
- restore_cache:
keys:
# "composer.lock" can be used if it is committed to the repo
- v1-dependencies-{{ checksum "composer.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run:
name: "Install Dependencies"
command: composer install -n --prefer-dist
- save_cache:
key: v1-dependencies-{{ checksum "composer.json" }}
paths:
- ./vendor
下一步是创建一个控制器。这个控制器将被用来处理来自CircleCI的请求,并根据你保存在数据库中的内容检索请求的历史。要创建一个控制器,运行这个artisan 命令。
php artisan make:controller CircleCIController
在新创建的app/Http/Controllers/CircleCIController.php 文件中,添加以下内容。
<?php
namespace App\Http\Controllers;
use App\Models\WebhookNotification;
use Illuminate\Http\{JsonResponse, Response};
class CircleCIController extends Controller {
public function getAllNotifications()
: JsonResponse {
return response()->json();
}
public function handleNotification()
: JsonResponse {
return response()
->json(null, Response::HTTP_NO_CONTENT);
}
}
这个代码片断声明了两个函数。目前,它们只分别返回状态为200 和204 的JSON响应。
接下来,为你的API添加两条路由。打开routes/api.php ,添加这个。
Route::post('circleci', [CircleCIController::class, 'handleNotification']);
Route::get('circleci', [CircleCIController::class, 'getAllNotifications']);
第一个路由是webhook路由。它将处理来自CircleCI的POST请求。第二个路由将被用来获取CircleCI发送给webhook的通知。因为这些路由已经被注册为API路由,完整的路由URI将以api:api/circleci 为前缀。
导入CircleCIController 。
use App\Http\Controllers\CircleCIController;
现在你已经准备好为你的应用程序提供服务。使用这个命令运行应用程序。
php artisan serve
设置ngrok
为了开发的目的,我们需要一种方法,将我们的本地应用程序作为一个webhook暴露给CircleCI。我们可以使用ngrok将我们正在运行的Laravel应用程序暴露在互联网上。
下载ngrok的可执行文件并解压。要在你的本地机器上暴露一个端口, 使用ngrok http 命令, 指定你想要暴露的端口号.我们的Laravel应用程序将在8000端口运行, 所以使用这个命令:
ngrok http 8000
当你启动ngrok时,它将在你的终端显示一个用户界面,显示你的隧道的公共URL和其他状态以及通过你的隧道连接的指标信息。

设置一个CircleCI管道
接下来,在GitHub上建立一个仓库,并将该项目链接到CircleCI。
登录你的CircleCI账户。如果你用GitHub账户注册,你的所有仓库将显示在你的项目仪表板上。
在你的circle_ci_webhook_api 项目旁边,点击Set Up Project。
CircleCI将检测项目中的config.yml 文件。点击使用现有的配置,然后点击开始构建。您的第一个构建过程将开始运行并成功完成。
为项目配置一个webhook
在circle_ci_webhook_api 项目的CircleCI仪表板上,点击项目设置。在侧边栏,点击Webhooks,然后点击Add Webhook。填写表格。

在Receiver URL字段中指定ngrok给出的公共URL。添加一个秘密令牌来验证对Webhook的传入请求,并确保只接受来自CircleCI的请求。记下秘密令牌,因为你将用它来验证进入接收器URL的请求。
点击添加Webhook来保存Webhook的细节。当一个工作流程或工作完成后,一个POST请求将被发送到我们Laravel应用程序的api/circleci 。
设置环境变量
在本教程中, 我们将使用MySQL来管理我们的数据库.在.env 文件中, 用这个来更新数据库相关的参数:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=circle_ci_webhook_api
DB_USERNAME=YOUR_DATABASE_USERNAME
DB_PASSWORD=YOUR_DATABASE_PASSWORD
使用你喜欢的数据库管理应用程序,创建一个名为circle_ci_webhook_api 的新数据库。
你还需要一个环境变量来保存webhook的秘密令牌。在.env 文件中包括以下内容。
CIRCLE_CI_WEBHOOK_SECRET="YOUR_CIRCLE_CI_WEBHOOK_SECRET"
创建WebhookNotification 模型
在我们的应用程序中,我们需要一个模型,它将表示由我们的webhook接收的通知的细节。将这个模型命名为WebhookNotification 。它将有这些字段。
id是数据库中的主键。notification_id是webhook提供的id,用于唯一地识别来自CircleCI的每一个通知。type的值是workflow-completed或job-completed。happened_at是代表事件发生时间的ISO 8601时间戳。has_vcs_info让你知道该通知是否有一个版本控制图,用于访问与触发该事件的git提交相关的元数据。commit_subject代表提交的主题,如果通知有一个版本控制图。否则该值为空。commit_author代表提交的作者。如果 ,该值可以为空。has_vcs_infoevent_status对应于工作流或工作达到终端状态时的状态。其值可以是 , , , , 或 。successfailederrorcanceledunauthorizedworkflow_url包含工作流或工作在CircleCI仪表板上的URL。
使用此命令创建模型。
php artisan make:model WebhookNotification -m
在新创建的database/migrations/*YYYY_MM_DD_HHMMSS*_create_webhook_notifications_table.php ,更新up 函数以匹配这个代码块。
public function up() {
Schema::create('webhook_notifications', function (Blueprint $table) {
$table->id();
$table->string('notification_id');
$table->string('type');
$table->string('happened_at');
$table->boolean('has_vcs_info');
$table->string('commit_subject')->nullable();
$table->string('commit_author')->nullable();
$table->string('event_status');
$table->text('workflow_url');
$table->timestamps();
});
}
接下来,打开WebhookNotification 的模型文件:app/Models/WebhookNotification.php 。使用这段代码更新它,将字段添加为$fillable 属性。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class WebhookNotification extends Model
{
use HasFactory;
protected $fillable = [
'notification_id', 'type', 'happened_at', 'has_vcs_info', 'commit_subject','commit_author',
'event_status','workflow_url'
];
}
运行你的迁移程序,创建webhook_notifications 表和它的列。
php artisan migrate
创建一个辅助类来管理CircleCI请求
为了分离关注点,使我们的控制器更加精简,你可以创建一个辅助类。这个帮助类验证传入的请求,并解析请求中用于填充WebhookNotification 模型的信息。
在项目的app 文件夹中,创建一个名为Helpers 的新文件夹。在该文件夹中,创建一个名为CircleCINotificationHelper.php 的新文件。用这段代码更新该文件的内容。
<?php
namespace App\Helpers;
use App\Models\WebhookNotification;
use Illuminate\Http\{Request, Response};
class CircleCINotificationHelper {
public static function handle(Request $request)
: void {
$circleCISignature = $request->headers->get('circleci-signature');
self::validate($circleCISignature, $request->getContent());
$requestContent = $request->toArray();
$hasVCSInfo = isset($requestContent['pipeline']['vcs']);
$notificationType = $requestContent['type'];
$notificationDetails = [
'notification_id' => $requestContent['id'],
'type' => $notificationType,
'happened_at' => $requestContent['happened_at'],
'workflow_url' => $requestContent['workflow']['url'],
'has_vcs_info' => $hasVCSInfo,
];
if ($hasVCSInfo) {
$commitDetails = $requestContent['pipeline']['vcs']['commit'];
$notificationDetails['commit_subject'] = $commitDetails['subject'];
$notificationDetails['commit_author'] = $commitDetails['author']['name'];
}
$notificationDetails['event_status'] = $notificationType === 'job-completed' ?
$requestContent['job']['status'] :
$requestContent['workflow']['status'];
$webhookNotification = new WebhookNotification($notificationDetails);
$webhookNotification->save();
}
private static function validate(string $signature, string $requestContent)
: void {
$receivedSignature = explode('=', $signature)[1];
$generatedSignature = hash_hmac(
'sha256',
$requestContent,
env('CIRCLE_CI_WEBHOOK_SECRET')
);
abort_if(
$receivedSignature !== $generatedSignature,
Response::HTTP_UNAUTHORIZED,
'Invalid Signature Provided'
);
}
}
CircleCINotificationHelper 只有一个名为handle 的公共方法,它接收一个Request 对象。
在解析请求的内容之前,首先使用validate 函数进行验证检查。这确保只有来自CircleCI的请求被处理。为了验证请求,circleci-signature 头与请求正文的HMAC-SHA256摘要进行比较,使用配置的签署秘密作为秘密密钥。如果数值不匹配,该过程将被终止,并返回一个401 响应。
如果请求签名检查出来,handle 方法检索WebhookNotification 模型的值,创建一个,并将其保存到数据库。
为CircleCIController添加功能
有了模型和助手,我们可以在CircleCIController中为先前定义的函数添加功能。打开app/Http/Controllers/CircleCIController.php ,并更新它以与之匹配。
<?php
namespace App\Http\Controllers;
use App\Helpers\CircleCINotificationHelper;
use App\Models\WebhookNotification;
use Illuminate\Http\{JsonResponse, Request, Response};
class CircleCIController extends Controller {
public function getAllNotifications()
: JsonResponse {
return response()->json(WebhookNotification::all());
}
public function handleNotification(Request $request)
: JsonResponse {
CircleCINotificationHelper::handle($request);
return response()
->json(null, Response::HTTP_NO_CONTENT);
}
}
现在这些改动已经完成,你可以通过提交和推送你的改动到GitHub仓库来触发一个新事件。
git add .
git commit -m 'Implement Webhook for CircleCI'
git push origin main
这就触发了一个构建过程。当这个过程完成后,会向我们指定的ngrok公共URL发送一个请求。使用ngrok创建的隧道,应用程序接收请求并将通知保存到数据库中。你可以在你的ngrok终端查看HTTP Requests 部分。

总结
在本教程中,我们设置了一个Laravel API来与CircleCI webhook进行通信。虽然我们只是将数据保存在数据库中,用于未来的可视化/分析,但webhooks为许多依赖实时数据的操作打开了大门,如事件检测和响应/管理。与你的团队分享这个教程,用你自己的项目来扩展你所学到的东西。