使用SmtpClient在.net中发送邮件

22

我无法使用SMTP客户端发送邮件。以下是代码:

SmtpClient client=new SmtpClient("Host");
client.Credentials=new NetworkCredential("username", "password");
MailMessage mailMessage = new MailMessage();
mailMessage.from="sender@gmail.com";
mailMessage.To.Add("recipient@gmail.com");
mailMessage.body="body";
mailMessage.subject="subject";
client.Send(mailMessage);

问题在于当我在ASP.NET应用程序中使用此代码时,我无法收到任何邮件。当我在ASP.NET中将发送方的电子邮件地址更改为NetworkCredential中给出的用户名时,我可以收到邮件。

但是,在C# Windows应用程序中,即使发件人的电子邮件地址无效,我也可以收到电子邮件。


这可能与服务器配置有关,也可能与您的开发机器有关。 - Nate
您似乎在发件人字段中使用了 Gmail 地址。您是否确保所有设置都按照他们的说明进行设置? - Tomas Aschan
5个回答

20

我发现在设置<html><span class="diff-delete">UseDefaultCredentials = false</span></html>之前设置SmtpClient凭据属性,UseDefaultCredentials = false会导致凭据被忽略。

失败:

SmtpClient smtp = new SmtpClient;
smtp.Credentials = new NetworkCredential("user","pass");
smtp.UseDefaultCredentials = false;

工作:

SmtpClient smtp = new SmtpClient;
smtp.UseDefaultCredentials = false;
smtp.Credentials = new NetworkCredential("user","pass");

好的,我会尽力为您翻译。以下是需要翻译的内容:

难以想象。

** 更新 **

这里顺序很重要的原因是,UseDefaultCredentials 属性上的 setter 实际上通过反编译代码将 Credentials 设置为 null

/// <summary>Gets or sets a <see cref="T:System.Boolean" /> value that controls whether the <see cref="P:System.Net.CredentialCache.DefaultCredentials" /> are sent with requests.</summary>
/// <returns>
/// <see langword="true" /> if the default credentials are used; otherwise <see langword="false" />. The default value is <see langword="false" />.</returns>
/// <exception cref="T:System.InvalidOperationException">You cannot change the value of this property when an e-mail is being sent.</exception>
public bool UseDefaultCredentials
{
  get
  {
    return this.transport.Credentials is SystemNetworkCredential;
  }
  set
  {
    if (this.InCall)
      throw new InvalidOperationException(SR.GetString("SmtpInvalidOperationDuringSend"));
    this.transport.Credentials = value ? (ICredentialsByHost) CredentialCache.DefaultNetworkCredentials : (ICredentialsByHost) null;
  }
}

这没有意义,因为两个代码片段几乎相同。唯一的区别是在第二个片段中,NetworkCredential类的实例化发生在using块内部。也许这会产生意想不到的副作用? - Alex Wiese
你说得对。而且,设置 UseDefaultCredentials = false 之前设置 Credentials 才是导致这种情况发生的原因。我会编辑帖子。 - user3524983
我终于让来自web.config的凭据工作了。哇!真是不容易的一天。 - user3524983
2
我没有意识到的是,设置主机和端口就像是在 web.config 的 smtp/network 条目中进行查找。一旦找到匹配项,它就会提取用户和密码并使用它们。希望这能帮助其他人。 - user3524983
感谢这个解决方案。我已经更新了答案,并附上了示例代码,展示为什么顺序很重要。 - Chris Snowden

7
短答案:不要使用.NET内部SMTP客户端类,而是使用MailKit
长答案:
1. 它被标记为MS文档中的过时

[System.Obsolete("SmtpClient及其网络类型设计不良,我们强烈建议您改用https://github.com/jstedfast/MailKithttps://github.com/jstedfast/MimeKit")] public class SmtpClient : IDisposable

  1. 使用oss lib MailKit
  2. 说明:因为您很快就会面对它的不足。在这里阅读或者在互联网上阅读。
  3. 这里有一个示例,用于发送纯文本消息和多部分消息。

我故意没有复制粘贴文档中的示例,因为文档可能随时间变化而改变,最好还是去阅读文档。


1

这意味着您的邮件服务器不允许 Mail-Relay。您的邮件服务器只允许您使用经过身份验证的电子邮件 ID 作为用户名发送邮件。通常,这是为了防止以已验证的身份之外的不同身份发送邮件。


1
邮件服务器在Windows和ASP.NET应用程序中是相同的。 而且相同的代码在ASP.NET应用程序的其他页面中运行良好,但在登录页面中无法正常工作。可能出了什么问题? - Manish Gupta
您的应用程序使用的是什么身份验证模式?请提供有关您的问题场景的更多细节。 - this. __curious_geek
在ASP.NET中,认证模式被设置为表单。 - Manish Gupta

0

试试这个:

MailMessage mail = new MailMessage("emailfrom","emailto");

mail.From = new MailAddress("emailfrom");
mail.Subject = txtsbjct.Text;
string Body = txtmsg.Text;
mail.Body = Body;

mail.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient();
smtp.Host = "smtp.gmail.com"; //Or Your SMTP Server Address
smtp.Credentials = new System.Net.NetworkCredential("youremail", "yourpassword");

smtp.EnableSsl = true;
smtp.Send(mail);
txtemail.Text = "";
txtmsg.Text = "";
txtsbjct.Text = "";
Label1.Text = "your email has been send";
mail = null;
smtp = null;

-4
void sendEmail(string strFrom
                        , string strTo
                        , string strSubject
                        , string strBody)
   {

       MailMessage objMailMessage = new MailMessage();
       System.Net.NetworkCredential objSMTPUserInfo =
           new System.Net.NetworkCredential();
       SmtpClient objSmtpClient = new SmtpClient();

       try
       {
           objMailMessage.From = new MailAddress(strFrom);
           objMailMessage.To.Add(new MailAddress(strTo));
           objMailMessage.Subject = strSubject;
           objMailMessage.Body = strBody;

           objSmtpClient = new SmtpClient("172.0.0.1"); /// Server IP
           objSMTPUserInfo = new System.Net.NetworkCredential
           ("User name", "Password","Domain");
           objSmtpClient.Credentials = objSMTPUserInfo;
           objSmtpClient.UseDefaultCredentials = false;
           objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;

           objSmtpClient.Send(objMailMessage);
       }
       catch (Exception ex)
       { throw ex; }

       finally
       {
           objMailMessage = null;
           objSMTPUserInfo = null;
           objSmtpClient = null;
       }
   }

5
你的错误处理实际上是一个非常糟糕的做法,因为你会丢失堆栈跟踪(参见https://dev59.com/NXRB5IYBdhLWcg3wET1J#730255)...此外,在这种情况下,省略catch子句是更好的选择。 - tsimbalar
3
确实。如果你想要catch (Exception ex)然后再throw它,只需使用throw;而不是throw ex;,这样堆栈跟踪就会被保留。然而,这只有在重新throw异常之前做其他事情(如记录日志)时才有用。在这种情况下,完全省略catch子句也会产生相同的效果。 - Owen Blacker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接