FastComments.com

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

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

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

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

支持的事件与资源 Internal Link


FastComments 仅支持 Comment 资源的 webhooks。

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

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


测试 Internal Link

在 Webhooks 管理页面中,每个事件类型(Create、Update、Delete)都有 Send Test Payload 按钮。Create 和 Update 事件会发送一个虚拟的 WebhookComment 对象,而测试 Delete 时会发送仅包含 ID 的虚拟请求体。

验证有效载荷

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

  1. token - 您的 API Secret
  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 发送的唯一结构是下面用 TypeScript 描述的 WebhookComment 对象。

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。 **/
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 = SSO 用户。 **/
10 type: 'user'|'sso'
11 /** 即使用户选择退出通知,该字段仍将设置为 true。 **/
12 sent: boolean
13}
14

HTTP 方法

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

  • Create 事件:POST 或 PUT(默认:PUT)
  • Update 事件:POST 或 PUT(默认:PUT)
  • Delete 事件:DELETE、POST 或 PUT(默认:DELETE)

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

请求头

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

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

请参阅 安全与 API 令牌 以获取有关验证 HMAC 签名的信息。


安全性与 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 集成易于理解且便于快速设置。

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