Symfony签名邮件报错:无法创建Email实例,如何解决?
问题:Symfony Mailer SMIME签名后发送失败报错
报错信息
Unable to send message with the "Symfony\Component\Mailer\Transport\AbstractApiTransport" transport: Unable to create an Email from an instance of "Symfony\Component\Mime\Message" as the body is too complex.
问题背景
移除SMIME签名功能后邮件可正常发送,但为了让邮件服务商优先处理邮件,需要保留签名功能并解决该报错。
邮件配置
MAILER_DSN=roundrobin(mailgun+api://*****55643c7e*****f8361b5*****27c-e61ae8dd-7*****:sandboxef6*****2742745ce*****c048689*****.mailgun.org@default?region=us)
相关代码
use Twig\Environment; use App\Core\MailStruct; use Psr\Log\LoggerInterface; use Symfony\Component\Mime\Email; use Twig\Loader\FilesystemLoader; use Symfony\Bridge\Twig\Mime\BodyRenderer; use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Crypto\SMimeSigner; use Symfony\Contracts\Service\Attribute\Required; public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } public function send(MailStruct $mail): void { $email = (new TemplatedEmail()) ->from('mailgun@*****.mailgun.org') ->to('b***@yahoo.com') ->replyTo('mailgun@*****.mailgun.org') ->priority(Email::PRIORITY_HIGH) ->subject('Time for Symfony Mailer!') ->htmlTemplate('test.html.twig') ->locale('de') ->context([ 'expiration_date' => '77', 'username' => 'stellaGregory', 'address' => '6 Kamasojo', ]); $loader = new FilesystemLoader('../templates/'); $twigEnv = new Environment($loader); $twigBodyRenderer = new BodyRenderer($twigEnv); $twigBodyRenderer->render($email); try { $signer = new SMimeSigner(__DIR__.'/certificate/certificate.crt', __DIR__.'/certificate/private.key'); $email = $signer->sign($email); $this->mailer->send($email); } catch (\Throwable $th) { throw $th; } }
解决方案
核心原因
报错根源在于你使用的Mailgun API传输(mailgun+api://)属于AbstractApiTransport,这类传输需要将邮件解析为结构化的Email对象来调用服务商API,但SMIME签名后,原TemplatedEmail会被转换为Symfony\Component\Mime\Message——这是一个完整的MIME消息对象,结构过于复杂,无法被API传输解析。
解决办法
切换到SMTP传输协议,SMTP传输直接发送完整的MIME消息,不需要解析邮件结构,完美支持签名后的复杂Message对象。
- 修改MAILER_DSN配置
将原API协议的DSN替换为SMTP协议的DSN,格式如下:
MAILER_DSN=mailgun+smtp://SMTP_USERNAME:SMTP_PASSWORD@default?region=us
SMTP_USERNAME和SMTP_PASSWORD可在Mailgun控制台的「SMTP凭证」板块获取(通常用户名是postmaster@your-domain.mailgun.org,密码是随机生成的字符串)。- 如果使用roundrobin负载均衡,格式为:
MAILER_DSN=roundrobin(mailgun+smtp://SMTP_USERNAME:SMTP_PASSWORD@default?region=us)
- 代码优化(可选)
原代码中手动实例化Twig环境和渲染器不符合Symfony最佳实践,可改为注入Twig服务:
// 构造函数注入Twig\Environment public function __construct(MailerInterface $mailer, private Environment $twigEnv) { $this->mailer = $mailer; } public function send(MailStruct $mail): void { $email = (new TemplatedEmail()) ->from('mailgun@*****.mailgun.org') ->to('b***@yahoo.com') ->replyTo('mailgun@*****.mailgun.org') ->priority(Email::PRIORITY_HIGH) ->subject('Time for Symfony Mailer!') ->htmlTemplate('test.html.twig') ->locale('de') ->context([ 'expiration_date' => '77', 'username' => 'stellaGregory', 'address' => '6 Kamasojo', ]); // 无需手动渲染,TemplatedEmail会自动通过Twig服务渲染 try { $signer = new SMimeSigner(__DIR__.'/certificate/certificate.crt', __DIR__.'/certificate/private.key'); $email = $signer->sign($email); $this->mailer->send($email); } catch (\Throwable $th) { throw $th; } }
内容的提问来源于stack exchange,提问作者DevOPlug
相关产品推荐
相关产品推荐

