MailMessage“From”属性赋值间接导致异常

时间:2013-09-01 11:56:19

标签: c# .net

这是我关于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建议的那样,使用干净的控制台应用程序很难重现这个问题。因此,我将尝试调查我的应用程序中发生的导致这种奇怪行为的行为。

2 个答案:

答案 0 :(得分: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. 运行此程序一次没有断点,然后注释 string s = from.ToString()行并再次运行。
  3. 如您所见,如果在调用Smtp.Send之前调用“ToString”方法,则会抛出异常。

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

    (为了使事情更清楚,只需简单地说:

    System.Diagnostics.Debug.WriteLine(from)
    

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

    问题:

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

    答案:

    出现在 .NET framework 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);
        }
    }
    

    此方法验证标头值在通过网络发送之前仅包含ANSI字符(由于SMTP协议限制)。但是,通过调用ToString(),“fullAddress”字段已经设置了非ANSI字符。因此,由于异常导致发送失败,表示标题中有“无效”字符。

    查看.NET 4.0的反映代码后,似乎通过完全重写MailAddress的内部实现来解决此问题!

    感谢您的帮助!

    P.S。 @ShadowWizard正如你所看到的,有时候什么都没有: - )

答案 1 :(得分:0)

我们遇到此问题但仅在从配置文件中读取发件人名称时才出现此问题。仅使用代码无法重现该问题。

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

解决方案是以明确的方式设置主题编码。