强制 System.Net.Mail.SmtpClient 使用 Base64 对主题标头进行编码
我们使用 .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"
);
当使用网络分析器检查主题标头的编码方式时,我们观察到以下情况
- :客户端连接到支持 SMTPUTF8 的 MTA,主题标头使用 UTF8 进行编码:
Subject: Caractèresaccentués
。 - 当客户端连接到不支持 SMTPUTF8 的 MTA 时,主题标头先以 Base64 编码,然后以 US-ASCII 编码:
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 编码,同时还使用 SmtpDeliveryFormat.International
,即使 SMTP 中继支持 SMTPUTF8 也是如此?我尝试这样做:
message.Subject = $"=?utf-8?B?{Convert.ToBase64String(Encoding.UTF8.GetBytes(subject))}?=";
但是主题标头没有通过,它由 SmtpClient
解码,然后 UTF8 编码为 Caractèresaccentués
,因此它不会改变任何内容。
We are using .NET Core 3.1's default SMTP client to send an email, like this:
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);
}
Then we call the method like this:
await this.SendMail(
from: "[email protected]",
to: "[email protected]",
subject: "Caractères accentués",
body: "dummy"
);
When using a network analyzer to check how the Subject header is encoded, we are observing the following:
- When the client connects to a MTA that supports SMTPUTF8, the Subject header is encoded using UTF8:
Subject: Caractères accentués
. - When the client connects to a MTA that does not support SMTPUTF8, the Subject header is encoded in Base64 then in US-ASCII:
Subject: =?utf-8?B?Q2FyYWN0w6hyZXMgYWNjZW50dcOpcw==?=
. - If we switch from
SmtpDeliveryFormat.International
toSmtpDeliveryFormat.SevenBits
, the Subject header is always encoded in Base64 then in US-ASCII (or maybe quoted-url then US-ASCII).
This is all standard and excpected behaviour.
We are using a third party email service as a SMTP relay, which supports SMTPUTF8 itself. But their service has a bug and fails to detect the lack of SMTPUTF8 support on recipient's side, resulting in email Subjects being improperly displayed in our clients mailboxes when they contain non-ASCII characters. They send the Subject with the same encoding we used with their MTA, which in our case is UTF8, because we need SmtpDeliveryFormat.International
(for compatibility with non-ASCII email addresses).
The issue disappears when we encode the Subject header using Base64, so we would like to do that as a workaround until our provider fixes the issue on their side. Using smtp.DeliveryFormat = SmtpDeliveryFormat.SevenBits
achieves this, but it also prevents us from using non-ASCII characters in email addresses, which is an even bigger problem than the Subject encoding issue. So we can't do that.
Is there a way to force the .NET client to use Base64 encoding for the Subject header while also using SmtpDeliveryFormat.International
, even when the SMTP relay supports SMTPUTF8? I tried to do this:
message.Subject = quot;=?utf-8?B?{Convert.ToBase64String(Encoding.UTF8.GetBytes(subject))}?=";
But the subject header is not passed through, it is decoded by the SmtpClient
then UTF8-encoded as Caractères accentués
, so it doesn't change anything.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
不是解决方案,而是解决方法:
如果、且仅当本地部分(即在目标邮件地址的“@”)包含非 ASCII 字符。这具有以下效果:
对于
[电子邮件受保护]
,主题将被正确编码。对于
recipient@éxample.com
,一切都会正常工作:MailAddress 类可以使用 punycode 对邮件地址的主机部分进行编码,而不需要 SMTPUTF8。对于
ré[电子邮件受保护]
,主题不会进行 Base64 编码。 但是,由于本地部分包含非 ASCII 字符,因此收件人邮件服务器极有可能支持 SMTPUTF8,因此可以正确解释您的主题行。< /p>示例代码:
Not a solution, but a workaround:
Set DeliveryFormat to
SmtpDeliveryFormat.International
if, and only if, the local part (i.e. the part before the "@") of the target mail address contains a non-ascii character. This has the following effect:For
[email protected]
, the subject will be properly encoded.For
recipient@éxample.com
, everything will work as well: The MailAddress class can encode the host-part of a mail address with punycode without requiring SMTPUTF8.For
ré[email protected]
, the subject won't be Base64-encoded. However, since local part contains a non-ASCII character, it is highly likely that the recipient mail server supports SMTPUTF8, and, thus, can interpret your subject line correctly.Example code: