[ PROMPT_NODE_24112 ]
Email Workers 常见陷阱
[ SKILL_DOCUMENTATION ]
# Email Workers 陷阱
## 关键问题
### ReadableStream 单次使用
typescript
// ❌ 错误:流被消耗了两次
const email = await PostalMime.parse(await new Response(message.raw).arrayBuffer());
const rawText = await new Response(message.raw).text(); // 为空!
// ✅ 正确:先缓存
const buffer = await new Response(message.raw).arrayBuffer();
const email = await PostalMime.parse(buffer);
const rawText = new TextDecoder().decode(buffer);
### ctx.waitUntil() 错误静默
typescript
// ❌ 错误被静默丢弃
ctx.waitUntil(fetch(webhookUrl, { method: 'POST', body: data }));
// ✅ 捕获并记录
ctx.waitUntil(
fetch(webhookUrl, { method: 'POST', body: data })
.catch(err => env.ERROR_LOG.put(`error:${Date.now()}`, err.message))
);
## 安全性
### 信封发件人 vs 头部发件人 (欺骗)
typescript
const envelopeFrom = message.from; // SMTP MAIL FROM (受信任)
const headerFrom = (await PostalMime.parse(buffer)).from?.address; // (不受信任)
// 使用信封发件人进行安全决策
### 输入验证
typescript
if (message.rawSize > 5_000_000) { message.setReject('Too large'); return; }
if ((message.headers.get('Subject') || '').length > 1000) {
message.setReject('Invalid subject'); return;
}
### 回复的 DMARC 要求
没有 DMARC,回复会静默失败。验证方式:`dig TXT _dmarc.example.com`
## 解析
### 地址解析
typescript
const email = await PostalMime.parse(buffer);
const fromAddress = email.from?.address || 'unknown';
const toAddresses = Array.isArray(email.to) ? email.to.map(t => t.address) : [email.to?.address];
### 字符编码
让 postal-mime 处理解码 - `email.subject`, `email.text`, `email.html` 均为 UTF-8。
## API 行为
### setReject() vs throw
typescript
// 使用 setReject() 进行 SMTP 拒绝
if (blockList.includes(message.from)) { message.setReject('Blocked'); return; }
// 使用 throw 处理 Worker 内部错误
if (!env.KV) throw new Error('KV not configured');
### forward() 仅限 X-* 头
typescript
headers.set('X-Processed-By', 'worker'); // ✅ 有效
headers.set('Subject', 'Modified'); // ❌ 被丢弃
### 回复需要已验证的域名
typescript
// 使用与接收地址相同的域名
const receivingDomain = message.to.split('@')[1];
await message.reply(new EmailMessage(`noreply@${receivingDomain}`, message.from, rawMime));
## 性能
### CPU 限制
typescript
// 跳过解析大型邮件
if (message.rawSize > 5_000_000) {