FastComments.com

使用 FastComments,可以在每当评论被添加、更新或从我们的系统中删除时调用一个 API 端点。

我们通过基于 HTTP/HTTPS 的异步 webhooks 来实现这一功能。

什么是 Webhook(网络回调) Internal Link

Webhook 是一种机制,或两套系统之间的集成,其中“生产者”(FastComments)触发一个事件 由“消费者”(您)通过 API 调用来消费该事件。

支持的事件与资源 Internal Link


FastComments 仅支持 Comment 资源的 webhooks。

我们支持针对评论的创建、删除和更新的 webhooks。

在我们的系统中,这些都被视为独立的事件,因此在 webhook 事件的语义 和结构上有所不同。


测试 Internal Link

In the Webhooks admin there are Send Test Payload buttons for each event type (Create, Update, Delete). The Create and Update events send a dummy WebhookComment object, while testing Delete will send a dummy request body with just an ID.

验证有效载荷

在测试您的 webhook 集成时,请确认传入请求包含以下头:

  1. token - 您的 API 密钥
  2. X-FastComments-Timestamp - Unix 时间戳(秒)
  3. X-FastComments-Signature - HMAC-SHA256 签名

使用 HMAC 签名验证以确保有效载荷的真实性。

测试工具

您可以使用像 webhook.sitengrok 这样的工具在开发期间检查传入的 webhook 有效载荷。

事件类型

  • Create Event: 当创建新评论时触发。默认方法:PUT
  • Update Event: 当编辑评论时触发。默认方法:PUT
  • Delete Event: 当删除评论时触发。默认方法:DELETE

每个事件在请求主体中包含完整的评论数据(有关有效载荷格式,请参见 数据结构)。

数据结构 Internal Link

通过 webhook 发送的唯一结构是 WebhookComment 对象,下面以 TypeScript 概述。

WebhookComment 对象结构

"Create" 事件结构

"create" 事件的请求体是一个 WebhookComment 对象。

"Update" 事件结构

"update" 事件的请求体是一个 WebhookComment 对象。

"Delete" 事件结构

"delete" 事件的请求体是一个 WebhookComment 对象。

变更(自 2023  11  14 日起)
之前 "delete" 事件的请求体仅包含评论 id。现在它包含删除时的完整评论。
WebhookComment 对象
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** 评论的 id。 **/
4 id: string
5 /** 标识评论线程的 id 或 URL。已规范化。 **/
6 urlId: string
7 /** 指向评论所在位置的 URL。 **/
8 url?: string
9 /** 留下评论的用户 id。如果是 SSO,会有租户 id 前缀。 **/
10 userId?: string
11 /** 留下评论的用户的电子邮件。 **/
12 commenterEmail?: string
13 /** 在评论小部件中显示的用户名。对于 SSO,可能是 displayName。 **/
14 commenterName: string
15 /** 原始评论文本。 **/
16 comment: string
17 /** 解析后的评论文本。 **/
18 commentHTML: string
19 /** 评论的外部 id。 **/
20 externalId?: string
21 /** 父评论的 id。 **/
22 parentId?: string | null
23 /** 留言时的 UTC 日期。 **/
24 date: UTC_ISO_DateString
25 /** 综合评分(赞 - 踩)。 **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** 如果用户在发表评论时已登录、或其验证了评论、或在发表评论时验证了会话,则为 true。 **/
30 verified: boolean
31 /** 评论被验证的日期。 **/
32 verifiedDate?: number
33 /** 是否被版主标记为已审核。 **/
34 reviewed: boolean
35 /** 头像的位置或 base64 编码。仅当 SSO 传递了 base64 值时才会是 base64。 **/
36 avatarSrc?: string
37 /** 评论是否被手动或自动标记为垃圾评论? **/
38 isSpam: boolean
39 /** 评论是否被自动判定为垃圾评论? **/
40 aiDeterminedSpam: boolean
41 /** 评论中是否包含图片? **/
42 hasImages: boolean
43 /** 在“最相关”排序方向下评论所在的页码。 **/
44 pageNumber: number
45 /** 在“最旧优先”排序方向下评论所在的页码。 **/
46 pageNumberOF: number
47 /** 在“最新优先”排序方向下评论所在的页码。 **/
48 pageNumberNF: number
49 /** 评论是自动还是手动批准的? **/
50 approved: boolean
51 /** 评论撰写时用户的区域设置代码(格式:en_us)。 **/
52 locale: string
53 /** 评论中被成功解析的 @提及 列表。 **/
54 mentions?: CommentUserMention[]
55 /** 评论来源的域名。 **/
56 domain?: string
57 /** 与此评论关联的可选审核组 id 列表。 **/
58 moderationGroupIds?: string[]|null
59}
60

当用户在评论中被标记时,这些信息会存储在名为 mentions 的列表中。该列表中的每个对象具有以下结构。

Webhook 提及对象
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** 用户 id。对于 SSO 用户,会加上您的租户 id 前缀。 **/
4 id: string
5 /** 最终的 @提及 标签文本,包含 @ 符号。 **/
6 tag: string
7 /** 原始的 @提及 标签文本,包含 @ 符号。 **/
8 rawTag: string
9 /** 被提及用户的类型。user = FastComments.com 账户。sso = SSOUser。 **/
10 type: 'user'|'sso'
11 /** 即使用户选择退出通知,此字段仍将被设置为 true。 **/
12 sent: boolean
13}
14

HTTP 方法

您可以在管理面板中为每个 webhook 事件类型配置 HTTP 方法:

  • Create Event: POST or PUT (default: PUT)
  • Update Event: POST or PUT (default: PUT)
  • Delete Event: DELETE, POST, or PUT (default: DELETE)

由于所有请求都包含 ID,Create 和 Update 操作默认是幂等的(PUT)。重复相同的 Create 或 Update 请求不应在您这端创建重复对象。

请求头

每个 webhook 请求包含以下请求头:

请求头 说明
Content-Type application/json
token 您的 API Secret
X-FastComments-Timestamp 请求签名时的 Unix 时间戳(秒)
X-FastComments-Signature HMAC-SHA256 签名 (sha256=<hex>)

有关验证 HMAC 签名的信息,请参阅 安全与 API 令牌


安全性与 API 令牌 Internal Link

FastComments 的 webhook 请求包含多种身份验证机制以确保安全。

发送的请求头

请求头 描述
token 您的 API Secret(用于向后兼容)
X-FastComments-Timestamp 请求签名时的 Unix 时间戳(秒)
X-FastComments-Signature 负载的 HMAC-SHA256 签名

HMAC 签名验证(推荐)

我们强烈建议验证 HMAC 签名,以确保 webhook payload 是真实且未被篡改。

Signature Format: sha256=<hex-encoded-signature>

签名的计算方法:

  1. 连接: timestamp + "." + JSON_payload_body
  2. 使用您的 API Secret 作为密钥计算 HMAC-SHA256
  3. 对结果进行十六进制编码

示例验证(Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(req, apiSecret) {
    const timestamp = req.headers['x-fastcomments-timestamp'];
    const signature = req.headers['x-fastcomments-signature'];

    if (!timestamp || !signature) {
        return false;
    }

    // 验证时间戳是否近期(在 5 分钟内)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // 防止重放攻击
    }

    // 验证签名
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

    return signature === `sha256=${expectedSignature}`;
}

示例验证(Python)

import hmac
import hashlib
import time
import json

def verify_webhook_signature(headers, body, api_secret):
    timestamp = headers.get('X-FastComments-Timestamp')
    signature = headers.get('X-FastComments-Signature')

    if not timestamp or not signature:
        return False

    # 验证时间戳是否近期
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # 验证签名
    payload = json.dumps(body, separators=(',', ':'))
    message = f"{timestamp}.{payload}"
    expected = hmac.new(
        api_secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()

    return signature == f"sha256={expected}"

示例验证(PHP)

function verifyWebhookSignature($headers, $body, $apiSecret) {
    $timestamp = $headers['X-FastComments-Timestamp'] ?? null;
    $signature = $headers['X-FastComments-Signature'] ?? null;

    if (!$timestamp || !$signature) {
        return false;
    }

    // 验证时间戳是否近期(在 5 分钟内)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // 验证签名
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

旧版身份验证

包含您 API Secret 的 token 请求头仍会出于向后兼容而发送。然而,我们建议迁移到 HMAC 验证以提高安全性,因为它可以防止重放攻击。


结论

本 Webhooks 文档到此结束。

我们希望您觉得 FastComments Webhook 集成易于理解且便于快速设置。

如果您认为已发现我们文档中的任何空白或不足,请在下方告知我们。