Symfony 6.2下使用Messenger异步发送DKIM签名模板邮件失败求助
Symfony 6.2下使用Messenger异步发送DKIM签名模板邮件失败求助
嘿,我之前碰到过类似的坑,核心问题其实是TemplatedEmail的模板渲染时机和DKIM签名时机不匹配,咱们慢慢理清楚:
问题根源
你现在直接对TemplatedEmail对象执行DKIM签名,但这个对象本身只是存了模板路径和上下文变量,并没有立刻渲染成实际的HTML内容。当$signer->sign($email)执行时,邮件对象里还没有真正的HTML/text内容,签名后的邮件自然就会触发"A message must have a text or an HTML part or attachments"这个错误。
而不加签名时,Symfony的Mailer会在发送(或者Messenger处理消息)的最后一步自动渲染模板,所以能正常工作。
解决方案
这里有两种靠谱的解决办法,你可以根据自己的场景选:
方法一:手动渲染模板,再签名
先把模板内容提前渲染好,用普通的Email对象承载内容,再进行签名发送:
// 注入Twig服务(控制器里可直接用$this->get('twig'),其他地方建议依赖注入) $twig = $this->container->get('twig'); // 提前渲染模板内容 $htmlContent = $twig->render('emails/signup_confirm.html.twig', ['item' => $item]); // 创建普通Email对象,设置渲染好的内容 $email = (new \Symfony\Component\Mime\Email()) ->from($fromEmail) ->to($toEmail) ->priority(\Symfony\Component\Mime\Email::PRIORITY_HIGHEST) ->subject('Subject') ->html($htmlContent); // 执行DKIM签名 $signer = new \Symfony\Component\Mailer\Signer\DkimSigner($content_from_key, 'domain.com', 'dkim'); $signedEmail = $signer->sign($email); // 发送邮件 try { $mailer->send($signedEmail); } catch (\Symfony\Component\Mailer\Exception\TransportExceptionInterface $e) { $logger->critical($e->getMessage()); }
方法二:利用事件监听,在模板渲染后自动签名
这种方法更优雅,适合需要批量处理签名的场景,核心是在邮件模板渲染完成后再执行签名:
- 创建事件监听器
// src/EventListener/DkimSigningListener.php namespace App\EventListener; use Symfony\Component\Mailer\Event\EmailEvent; use Symfony\Component\Mailer\Signer\DkimSigner; class DkimSigningListener { private $dkimSigner; public function __construct(DkimSigner $dkimSigner) { $this->dkimSigner = $dkimSigner; } public function onEmailSend(EmailEvent $event): void { $email = $event->getMessage(); // 给需要签名的邮件加个自定义头标记,避免给所有邮件签名 if ($email->getHeaders()->has('X-Need-Dkim-Sign')) { $signedEmail = $this->dkimSigner->sign($email); $event->setMessage($signedEmail); } } }
- 配置监听器
在config/services.yaml里添加:
services: App\EventListener\DkimSigningListener: arguments: $dkimSigner: '@Symfony\Component\Mailer\Signer\DkimSigner' tags: - { name: kernel.event_listener, event: symfony.mailer.email.send, method: onEmailSend }
- 发送邮件时添加标记
$email = new \Symfony\Component\Mime\TemplatedEmail(); $email ->from($fromEmail) ->to($toEmail) ->priority(\Symfony\Component\Mime\Email::PRIORITY_HIGHEST) ->subject('Subject') ->htmlTemplate('emails/signup_confirm.html.twig') ->context(['item' => $item]) // 添加自定义头,告诉监听器需要签名 ->getHeaders()->addTextHeader('X-Need-Dkim-Sign', 'true'); try { $mailer->send($email); } catch (\Symfony\Component\Mailer\Exception\TransportExceptionInterface $e) { $logger->critical($e->getMessage()); }
这样一来,当Messenger异步处理邮件时,会先渲染模板,触发symfony.mailer.email.send事件,监听器此时拿到的是已经有完整HTML内容的邮件,签名后再发送就不会报错了。
额外注意点
- 确保你的DKIM私钥内容正确,不要漏掉
-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----这些标记 - 确认DKIM的selector(你这里用的是
dkim)和域名domain.com在DNS上的配置完全匹配
备注:内容来源于stack exchange,提问作者Pavel
相关产品推荐
相关产品推荐

