MimeMessage 导致 Gmail Apis 中需要收件人地址错误可能是由于 base64url 编码

MimeMessage causing recipient address required error in Gmail Apis possibly due base64url encoding

编辑 1:进一步研究后发现 space 不是罪魁祸首,但可能是 base64url 编码方法。我提取了纯文本和 MIME 消息的 Raw 字符串,并在在线 tool 中进行了比较。在纯文本的情况下,它们都是相同的,但在 mime 消息的情况下,它们完全不同,但是如果我将它们解码回字符串,它们是相同的。我对编码解码知之甚少,因此将不胜感激。 编辑结束。

我正在尝试使用 mimekit 在 c# 中撰写电子邮件并使用 google.apis.gmail.v1 发送。但它抛出错误“需要收件人地址[400]。这是代码。

var mailMessage = new MailMessage
            {
                Subject = "test subject",
                Body = " <h1>test body</h1>"
            };
            mailMessage.IsBodyHtml = true;
            mailMessage.To.Add("testemail");

            MimeMessage mm = MimeMessage.CreateFromMailMessage(mailMessage);
            var gmailMessage = new Google.Apis.Gmail.v1.Data.Message
            {
                Raw = Base64UrlEncode(mm.ToString())
            };
            service.Users.Messages.Send(gmailMessage, "me").execute();
            // Encoding method
             public static string Base64UrlEncode(string text)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(text);

            return Convert.ToBase64String(bytes)
                .Replace('+', '-')
                .Replace('/', '_')
                .Replace("=", "");
        }

上面的代码会抛出错误,但是如果我使用如下纯文本撰写电子邮件:

string plainText = "To:testemail\r\n" +
                        "Subject: test subject\r\n" +
                        "Content-Type: text/html; charset=us-ascii\r\n\r\n" +
                        "<h1>Test body<h1>";

如果我对纯文本进行编码并将其传递给 Gmail 邮件的原始 属性,这将完美无缺。 为了调试问题,我使用在线工具解码了纯文本和 MIME 消息,我注意到的唯一区别是 space 之前的 header 值,如下所示:

// plain text:
To:testemail

//mime message:
To: testemail

注意“收件人:”后的 space。为了证实我的怀疑,我从 mime 消息中删除了 space 并使用在线工具再次对其进行编码,并将编码后的字符串传递给 Raw 属性 并且它可以正常工作。所以想出了一个 hacky 解决方案,并尝试使用正则表达式删除 space,如下所示:

            var corrected = Regex.Replace(mm.ToString(), @"To: ", "To:");

但出于某种原因,它不起作用,这对我来说没有意义。根据文档,Raw 属性 采用 RFC 2822 格式和 base64url 编码的字符串,据我所知,RFC 2822 不允许 space 在 header 类型之后。任何帮助将不胜感激 :) 作为参考,这里的两个字符串都是从原始解码的,而其他的则没有;

//plain text that works:
To:test@test.com
Subject: Test subject
Content-Type: text/html; charset=us-ascii

<h1>Test body <h1>

// mime message that throw error
To: test@test.com
Subject: test message
Date: Date
MIME-Version: 1.0
Content-Type: text/html; charset=us-ascii

<h1>test body</h1>

终于解决了!!由于某种原因,MimeMessage 上的 ToString 方法在 mime 消息的字符串表示形式的开头和结尾添加了回车符 return 和换行符 (\r\n),例如“\r\n----- --string--------\r\n" 这显然会阻塞 gmail api 所以我只是像这样使用 trim 方法 mm.ToString().Trim('\r', '\n') 瞧,它完美地工作。不确定它是错误还是预期行为,但 Jstedfast 如果您有机会阅读本文 post 请提供您的反馈。

正确的解法应该是这样的:

var mailMessage = new MailMessage
{
    Subject = "test subject",
    Body = " <h1>test body</h1>"
};
mailMessage.IsBodyHtml = true;
mailMessage.To.Add("testemail");

MimeMessage mm = MimeMessage.CreateFromMailMessage(mailMessage);
byte[] rawMimeData;
using (var memory = new MemoryStream ()) {
    mm.WriteTo(memory);
    rawMimeData = memory.ToArray();
}

var gmailMessage = new Google.Apis.Gmail.v1.Data.Message
{
    Raw = Base64UrlEncode(rawMimeData)
};

service.Users.Messages.Send(gmailMessage, "me").execute();

// Encoding method
public static string Base64UrlEncode(byte[] bytes)
{
    return Convert.ToBase64String(bytes)
        .Replace('+', '-')
        .Replace('/', '_')
        .Replace("=", "");
}