我们使用 .NET Core 3.1 的默认 SMTP 客户端发送电子邮件,如下所示:
private async Task SendMail(string from, string to, string subject, string body)
{
var message = new MailMessage();
message.From = new MailAddress(from);
var toAddress = new MailAddress(to);
message.To.Add(toAddress);
message.Subject = subject;
message.SubjectEncoding = Encoding.UTF8;
message.Body = body;
message.BodyEncoding = Encoding.UTF8;
message.IsBodyHtml = false;
using var smtp = new SmtpClient();
smtp.Host = SMTP_HOST;
smtp.Port = SMTP_PORT;
smtp.EnableSsl = false;
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.DeliveryFormat = SmtpDeliveryFormat.International;
smtp.UseDefaultCredentials = false;
smtp.Credentials = new NetworkCredential(SMTP_USER, SMTP_PASSWORD);
await smtp.SendMailAsync(message);
}
然后我们这样调用该方法:
await this.SendMail(
from: "[email protected]",
to: "[email protected]",
subject: "Caractères accentués",
body: "dummy"
);
当使用网络分析仪检查主题标头的编码方式时,我们观察到以下情况:
Subject: Caractères accentués
。Subject: =?utf-8?B?Q2FyYWN0w6hyZXMgYWNjZW50dcOpcw==?=
。SmtpDeliveryFormat.International
切换到 SmtpDeliveryFormat.SevenBits
,Subject 标头始终先以 Base64 编码,然后以 US-ASCII 编码(或者可能先以引用 url 编码,然后以 US-ASCII 编码)。这都是标准和预期的行为。
我们使用第三方电子邮件服务作为 SMTP 中继,该服务本身支持 SMTPUTF8。但他们的服务有一个错误,无法检测到收件人方面缺乏 SMTPUTF8 支持,导致电子邮件主题在包含非 ASCII 字符时在我们的客户邮箱中显示不正确。他们使用我们在 MTA 中使用的相同编码发送主题,在我们的例子中是 UTF8,因为我们需要
SmtpDeliveryFormat.International
(为了与非 ASCII 电子邮件地址兼容)。
当我们使用 Base64 对主题标头进行编码时,该问题就会消失,因此我们希望将其作为一种解决方法,直到我们的提供商修复该问题为止。使用
smtp.DeliveryFormat = SmtpDeliveryFormat.SevenBits
可以实现这一点,但它也阻止我们在电子邮件地址中使用非 ASCII 字符,这是一个比主题编码问题更大的问题。所以我们不能这么做。
有没有办法强制 .NET 客户端对主题标头使用 Base64 编码,同时 also 使用
SmtpDeliveryFormat.International
,即使 SMTP 中继支持 SMTPUTF8?我尝试这样做:
message.Subject = $"=?utf-8?B?{Convert.ToBase64String(Encoding.UTF8.GetBytes(subject))}?=";
但是主题头没有被传递,它被
SmtpClient
解码,然后 UTF8 编码为 Caractères accentués
,所以它不会改变任何东西。
不是解决方案,而是解决方法:
当且仅当
目标邮件地址的本地部分(即“@”之前的部分)包含非 ASCII 字符时,将 DeliveryFormat 设置为
SmtpDeliveryFormat.International
。这具有以下效果:
,主题将被正确编码。
recipient@éxample.com
,一切都会正常工作:MailAddress 类可以使用 punycode 对邮件地址的主机部分进行编码,而不需要 SMTPUTF8。
,主题不会进行 Base64 编码。但是,由于本地部分包含非 ASCII 字符,因此收件人邮件服务器“极有可能”支持 SMTPUTF8,因此可以正确解释您的主题行。 示例代码:
public static void EnableNonAsciiMailAddressesIfNecessary(
SmtpClient client, MailMessage message)
{
var allAddresses =
(message.From != null
? new[] { message.From }
: Array.Empty<MailAddress>()).
Concat(message.To).
Concat(message.CC).
Concat(message.Bcc).
Concat(message.ReplyToList);
var smtpUtf8Required = allAddresses.Any(a => a.User.Any(c => c > 0x7f));
if (smtpUtf8Required)
{
client.DeliveryFormat = SmtpDeliveryFormat.International;
}
}