发送失败可能留下不可重试的已注册记录
原文依据:4 处示例先写入用户和邮箱映射,再等待外部邮件发送。Motoko 在 await 边界提交此前状态;如果发送随后返回错误,错误分支的 trap 不会撤销 await 前的写入。下一次调用又会因“用户或邮箱已注册”而被拒绝。
临时邮件故障可能永久占用该调用者和邮箱的注册位置,但用户从未收到验证链接,造成账户数据不一致和注册阻断。
示例在邮件发送前写入 users 和 emailToPrincipal,然后跨越 await;发送失败后执行 trap。按 Motoko 的 await 提交边界,这个 trap 不会回滚此前已提交的映射更新,而重试会被“用户已注册”或“邮箱已注册”检查拒绝。因此照搬示例可能使用户无法自行重试验证邮件。用户可要求作者提供失败补偿、独立重发端点或在持久状态中明确记录可重试状态。
public shared ({ caller }) func registerUser(email : Text, name : Text) : async () { if (users.containsKey(caller)) { Runtime.trap("User already registered"); }; if (emailToPrincipal.containsKey(email)) { Runtime.trap("Email already registered"); };查看另外 3 个位置
}; users.add(caller, user); emailToPrincipal.add(email, caller); let result = await EmailClient.sendVerificationEmail( "no-reply", [email], "Welcome to Our Service", "Hello " # name # ",<br><br>Thank you for registering with our service. Please <a href=\"{{VERIFICATION_URL}}\">click here</a> to verify your email address<br><br>Best regards,<br>The Team", ); switch (result) { case (#ok) {}; case (#err(error)) { Runtime.trap("Couldn't send verification email: " # error); }; }; }; public shared ({ caller }) func registerUser(email : Text, name : Text) : async () { if (users.containsKey(caller)) { Runtime.trap("User already registered"); }; if (emailToPrincipal.containsKey(email)) { Runtime.trap("Email already registered"); };