Skip to the content.

SMTP 与命名模板设计

使用方式和参数见组件 README。SMTP 负责单次发送及接受回执;发送任务、重试决策和最终投递反馈由宿主管理。模板只生成字符串,不承担通知调度。

SMTP 接受与资源释放

每次调用创建独立 MailKit 客户端及 MimeKit 消息,避免共享连接状态。地址必须是单一 mailbox,显示名单独传入;地址头、显示名和主题拒绝 CR、LF、NUL。To、Cc、Bcc 共同构成 SMTP 信封,允许仅有 Bcc;Bcc 不写入发送的 MIME 头。纯文本与 HTML 同时存在时生成 MIME alternative,换行遵循 MIME/SMTP 规则,单一正文可能带末尾 CRLF。

连接默认明确要求 STARTTLS,服务端不支持时失败,不降级。连接即 TLS 使用 SslOnConnect;只有显式无 TLS 配置才建立明文连接,且此配置禁止携带认证凭据。证书使用系统默认验证,公开构造器不接受任意证书。凭据配置为普通类,避免 record 自动字符串输出密码;组件不记录凭据。

发送过程有两个需要区分的结果:

阶段 行为
尚未收到接受响应 连接、认证、收件人拒绝、传输或取消异常向调用方传播;释放失败保留原错误
已收到成功接受响应 生成包含 Message-Id、原始服务端响应和接受时间的回执;使用独立有限超时尽力 QUIT、断开并释放

接受后的 QUIT 失败、清理超时或调用方取消不再把消息变成发送失败,防止宿主据此重发。MailKit 会容忍部分 QUIT 网络与协议错误,回执不保证收到 QUIT 确认,也不提供清理结果作为业务决策依据。I/O 超时约束客户端等待,调用方 token 覆盖接受前的异步操作。

等待最终接受响应时断线或取消,远端可能已经接受;本地异常不能证明发送未发生。服务器接受也不能证明邮箱送达、用户读取或后续转发成功。因此组件不自动重试,也不把清理结果当作投递反馈。

模板扫描与编码

扫描器按顺序查找双花括号,一次替换每个 token。名称采用点分 ASCII 标识符,各段为 [A-Za-z_][A-Za-z0-9_]*。缺失值抛出 KeyNotFoundException;非法、未闭合或游离闭合 token 抛出 FormatException。名称比较沿用调用方字典的比较器,null 值写为空字符串。

替换值不会再次进入扫描器,所以值中的 `` 保留字面内容,不形成递归或代码执行。普通单花括号保留;双花括号属于模板语法,需要字面双花括号时可通过值传入。输出在内存中构建,宿主应限制模板和值的总大小。

HTML 模式使用 WebUtility.HtmlEncode 编码值,保留模板标签。该策略只适用于 HTML 文本节点;JavaScript、CSS、URL 上下文需要各自的编码与校验,模板本身仍由宿主管理。

依赖与验证

SMTP 使用 MailKit 4.18.1 及其传递依赖,许可证见 MIT 声明。模板实现只使用 .NET 标准库。Notifications 对 Core 的项目引用用于站内通知文件写入,SMTP 不使用 Core 执行传输。

本地回环 SMTP 测试验证真实协议、MIME、收件人、拒绝、取消及接受后清理失败。TLS 正向测试通过内部工厂把实际客户端锁定到临时证书的 SHA-256 指纹;另测公开构造器拒绝该自签名证书。测试不修改系统证书信任,也不向外部收件人发送消息。