1

这是我在 StackOverflow 上的第一个问题!

但是,我(偶然)解决了这个问题,但并不真正理解奇怪的行为。

以下代码引发异常:

...
MailMessage ms = new MailMessage();
MailAddress from = new MailAddress("my@email.address", "my display name (in hebrew)");
ms.From = from;
...

我得到的例外是:

"Failure sending mail."
(The inner exception message is: "An invalid character was found in header value.")

但是,如果我将新实例直接分配给“From”属性(如您在下面的代码片段中所见),一切都会按预期工作。

...
MailMessage ms = new MailMessage();
ms.From = new MailAddress("my@email.address", "my display name (in hebrew)");
...

有谁有想法吗?

谢谢 :-)

编辑1:重现步骤

正如@ShadowWizard 建议的那样,使用干净的控制台应用程序很难重现此问题。因此,我将尝试调查导致这种奇怪行为的应用程序中发生了什么。

4

2 回答 2

3

好的,

现在我知道如何重现这个问题以及如何解决它(或者更确切地说是如何避免它)。

要重现,请创建一个空的 .NET 3.5 控制台应用程序并复制以下代码。

namespace Example
{
    class Program
    {
        private static SmtpClient _smtp = null;
        public static SmtpClient Smtp
        {
            get
            {
                if (_smtp == null)
                {
                    _smtp = new SmtpClient("relay.your-smtp-host.com", 25);
                    _smtp.UseDefaultCredentials = true;
                }
                return _smtp;
            }
        }

        static void Main(string[] args)
        {
            using (MailMessage ms = new MailMessage())
            {
                var from = new MailAddress("from@email-address.com", "**THE NAME MUST CONTAINS SOME NON ANSI CHARS**");

                string s = from.ToString(); /* This is the line - comment it and everything will work fine! */

                ms.From = from;
                ms.To.Add(new MailAddress("recipient@address.co.il"));
                ms.Subject = "Here is my message title";
                ms.Body = "Here is my message body";
                Smtp.Send(ms);
            }
        }
    }
}

笔记:

  1. 要重现此问题,您必须在 DisplayName 属性中放置一些非 ANSI 字符。
  2. 在没有断点的情况下运行该程序一次,然后注释行字符串 s = from.ToString()并再次运行。

如您所见,如果在调用 Smtp.Send 之前调用了“ToString”方法,则会引发异常。

好吧,我首先遇到了这个问题,因为我使用断点运行应用程序,所以调试器在后台调用 ToString() 方法。

(为了使事情更清楚,只需输入以下行:

System.Diagnostics.Debug.WriteLine(from)

在调用 Smtp.Send 之前,然后在调用之后移动它)。

问题:

这种奇怪行为的原因是什么?

回答:

结果表明,在.NET 框架 3.5中,MailAddress 类中有两种方法,它们以两种不同的方式设置名为“fullAddress”的内部字段。

第一种方法是 ToEncodedString():

internal string ToEncodedString()
{
    if (this.fullAddress == null)
    {
        if ((this.encodedDisplayName != null) && (this.encodedDisplayName != string.Empty))
        {
            StringBuilder builder = new StringBuilder();
            MailBnfHelper.GetDotAtomOrQuotedString(this.encodedDisplayName, builder);
            builder.Append(" <");
            builder.Append(this.Address);
            builder.Append('>');
            this.fullAddress = builder.ToString();
        }
        else
        {
            this.fullAddress = this.Address;
        }
    }
    return this.fullAddress;
}

第二种方法是 ToString():

public override string ToString()
{
    if (this.fullAddress == null)
    {
        if ((this.encodedDisplayName != null) && (this.encodedDisplayName != string.Empty))
        {
            StringBuilder builder = new StringBuilder();
            if (this.DisplayName.StartsWith("\"") && this.DisplayName.EndsWith("\""))
            {
                builder.Append(this.DisplayName); 
            }
            else
            {
                builder.Append('"');
                builder.Append(this.DisplayName);
                builder.Append('"');
            }
            builder.Append(" <");
            builder.Append(this.Address);
            builder.Append('>');
            this.fullAddress = builder.ToString();
        }
        else
        {
            this.fullAddress = this.Address;
        }
    }
    return this.fullAddress;
}

现在,在 SmtpClient 中,当发送消息时,它会触发“From”标头的设置:

this.Headers[MailHeaderInfo.GetString(MailHeaderID.From)] = this.From.ToEncodedString();

这又会触发以下方法:

public override void Set(string name, string value)
{
    if (Logging.On)
    {
        Logging.PrintInfo(Logging.Web, this, "Set", name.ToString() + "=" + value.ToString());
    }
    if (name == null)
    {
        throw new ArgumentNullException("name");
    }
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (name == string.Empty)
    {
        throw new ArgumentException(SR.GetString("net_emptystringcall", new object[] { "name" }), "name");
    }
    if (value == string.Empty)
    {
        throw new ArgumentException(SR.GetString("net_emptystringcall", new object[] { "value" }), "name");
    }
    if (!MimeBasePart.IsAscii(name, false))
    {
        throw new FormatException(SR.GetString("InvalidHeaderName"));
    }
    if (!MimeBasePart.IsAnsi(value, false))
    {
        throw new FormatException(SR.GetString("InvalidHeaderValue"));
    }
    name = MailHeaderInfo.NormalizeCase(name);
    MailHeaderID iD = MailHeaderInfo.GetID(name);
    if ((iD == MailHeaderID.ContentType) && (this.part != null))
    {
        this.part.ContentType.Set(value.ToLower(CultureInfo.InvariantCulture), this);
    }
    else if ((iD == MailHeaderID.ContentDisposition) && (this.part is MimePart))
    {
        ((MimePart) this.part).ContentDisposition.Set(value.ToLower(CultureInfo.InvariantCulture), this);
    }
    else
    {
        base.Set(name, value);
    }
}

This method validates that the header value contains only ANSI character before sending it through the network (because of SMTP protocol limitations). But the "fullAddress" field was already set with non ANSI characters by a call to ToString(). Hence the sending was failed due to exception, saying that there are "Invalid" characters in the header.

After looking in the reflected code of .NET 4.0 it seems that this issue was solved by totally rewriting the internal implementation of MailAddress!

Thanks for trying to help!

P.S. @ShadowWizard As you can see, sometimes there is nothing :-)

于 2013-09-01T19:57:10.373 回答
0

我们遇到了这个问题,但只有在从配置文件中读取发件人名称时。仅使用代码无法重现该问题。

http://netpl.blogspot.com/2008/09/smtpclient-exception-invalid-character.html

解决方案是以显式方式设置主题编码。

于 2013-09-01T19:06:30.883 回答