用手动模式自己驱动验证
到这篇教程结束时,你将从你自己的代码、而非内置 UI 来驱动 Caputchin 验证生命周期,在两个元素中适合你情况的那一个上。手动模式(trigger="manual")存在于两个不同的元素上,而它们干的活儿确实不同:
| 元素 | 手动模式给你什么 | 你驱动 |
|---|---|---|
<caputchin-widget> | 内置复选框被隐藏;你从你 自己的触发(一次表单提交、一个自定义按钮)触发 proof-of-work 求解。无游戏。 | start()(然后求解通常自行结清) |
<caputchin-game> | 无 iframe;你把 你自己的游戏标记 嵌进组件的布局外壳,并从玩法决定结果。 | pass() / fail()(无 start();回合一挂载即上线) |
按你要回答的问题来选:
- “我想要正常的隐形/复选框验证,但由我自己的手势触发,且不带默认复选框。” → 下面的 复选框组件。
- “我想要我自己的交互式挑战渲染在我自己的 DOM 里,而非一个 iframe 游戏,且由我决定通过/失败。” → 下面的 游戏宿主。
游戏路径适配在哪里,见 跑你自己的游戏。你的页面上需要已经有组件;如果还没有,就先去 添加组件。
两者共有的一个局限:一个手动模式的回合 无法满足一个 游戏关卡。复选框元素根本从不给一个密钥设关卡(只有 <caputchin-game> 能),而一个手动的 <caputchin-game> 回合没有可回放的轨迹,所以一个设了关卡的密钥拒绝它。手动模式是给未设关卡的密钥(proof-of-work 验证)或纯游戏用途的。
路径 A:复选框组件,你自己的触发
当你想要标准验证、但想自己触发它时用这个,例如只在访客提交一个表单时跑求解,或者放在你自己的按钮后面而非内置复选框。
1. 把元素标为手动
<caputchin-widget id="cap" sitekey="cpt_pub_..." trigger="manual"></caputchin-widget>
<button id="go" type="button">Verify and continue</button>有了 trigger="manual",内置复选框 UI 被隐藏;元素等你来开始求解。
2. 从你的手势开始求解
手动模式下的复选框元素暴露单一一个方法,start():
start()踢动 proof-of-work 求解,取代复选框点击。在trigger="manual"下有效。
复选框组件没有 pass() / fail();你一旦调用 start(),求解就自行结清并发出 pass 事件。(释放/中止把手在游戏宿主上,在路径 B 里讲到。)
const widget = document.getElementById("cap");
document.getElementById("go").addEventListener("click", () => {
widget.start(); // begin verification now
});
widget.addEventListener("pass", () => {
// token issued; submit your form / continue
});当求解结清时,元素完成验证,并(在一个表单里)像任何其他组件那样注入 caputchin-token 字段。你的后端 核验那个令牌 一如往常;手动模式在服务端什么都不改。
路径 B:游戏宿主,你自己的 DOM
当你想把你自己的交互式挑战渲染在你自己的标记里、而非加载一个 iframe 游戏,并自己决定结果时用这个。
1. 把元素标为手动并嵌入你的标记
在 <caputchin-game> 上放 trigger="manual",并把你自己的标记放进它里面。在其他每个模式里组件都忽略 light-DOM 子元素;手动模式是它通过一个 <slot> 把它们投影进它布局外壳的那唯一一个模式。
<caputchin-game id="cap" sitekey="cpt_pub_..." trigger="manual">
<!-- Your own markup. The widget renders it inside its shell. -->
<div id="my-game">
<p>Tap the banana three times.</p>
<button id="banana" type="button">🍌</button>
</div>
</caputchin-game>在有一个 sitekey 存在、且密钥未设关卡时,组件在后台跑它的 proof-of-work 检查;你的交互是看得见的那部分。在没有 sitekey(或 no-verify)时,它是纯游戏:你的交互跑起来,没有什么可验证。
2. 从你的代码驱动结果
手动模式下的游戏宿主暴露 pass() 和 fail()。没有 start():回合在元素一挂载时就上线(工厂被挂载就是那个开始信号)。
pass(payload?)释放验证:访客成功了。调用它一次;之后的调用被忽略(裁定已锁定)。fail(payload?)中止回合。可选;一个未完成的回合无论如何都被当作非通过。
那些载荷:
pass({ trace }):trace是你的交互产出的那条不透明的回合记录。在一个设了关卡的回合上,服务器重放它以得裁定;在一个未设关卡或纯游戏的密钥上,它被原样接受,所以当没有什么可回放时传一个空字符串(或一个简单的标记)。这里没有客户端提供的分数。fail({ code?, message? }):一个可选的诊断code和message,两者都是客户端元数据,绝非一个信任信号。
const widget = document.getElementById("cap");
let taps = 0;
document.getElementById("banana").addEventListener("click", () => {
taps += 1;
if (taps >= 3) {
widget.pass({ trace: `banana:${taps}` }); // visitor won
}
});
// Optional: give up explicitly.
// widget.fail({ code: "gave-up", message: "closed the prompt" });和复选框路径一样,pass() 完成验证并注入令牌;你的后端以同样的方式核验它。
对结果作出反应(两条路径)
监听组件的事件来更新你自己的 UI:
widget.addEventListener("pass", () => {
// verification released
});
widget.addEventListener("error", (e) => {
console.warn("verification error", e.detail);
});那个信任决定始终是你的后端确认令牌,而非那个事件;事件是给 UX 的。
手动模式做不到什么
- 给一个站点密钥设关卡。 一个打开了 要求一个游戏 的密钥根本无法被复选框元素满足,而且拒绝一个手动的
<caputchin-game>(它在搭建时抛出一个配置错误),因为没有可回放的轨迹。手动模式是给未设关卡或纯游戏的密钥的。 - 重放一个自定义游戏的回合。 如果你需要一个设关卡的游戏,就把它渲染成一个 自托管 iframe 游戏 并产出一条轨迹,而不是手动模式。
另见
- 组件方法与事件:
start()/pass()/fail()以及它们发出的每个事件的完整参考。 - 自托管 iframe 游戏:一个能设关卡的、沙箱化的、可回放的游戏。
- 跑你自己的游戏:两种交付模式的对比。
- 在你的后端核验:确认一次通过产出的那个令牌。