You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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());
}

方法二:利用事件监听,在模板渲染后自动签名

这种方法更优雅,适合需要批量处理签名的场景,核心是在邮件模板渲染完成后再执行签名:

  1. 创建事件监听器
// 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);
        }
    }
}
  1. 配置监听器
    在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 }
  1. 发送邮件时添加标记
$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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 09:29:33