ASP.NET MailKit SMTP响应 [英] ASP.NET MailKit SMTP response

查看:100
本文介绍了ASP.NET MailKit SMTP响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有此代码已成功发送电子邮件-除了前一天没有发送电子邮件.因此,我想检查SMTP响应,但不确定该怎么办.

I have this code which has been successfully sending emails - except for the other day when it did not. Therefore, I'd like to check the SMTP response, but not sure what to do.

这是我的代码:

using (var client = new SmtpClient())
{
  client.LocalDomain = "xxxxxxxxxxxx";
  await client.ConnectAsync("xxxxxxxxxxxx", xx, SecureSocketOptions.StartTls).ConfigureAwait(false);
  await client.AuthenticateAsync( new System.Net.NetworkCredential("xxxxxxxxxxxx", "xxxxxxxxxxxx")).ConfigureAwait(false);
  await client.SendAsync(emailMessage).ConfigureAwait(false);
  await client.DisconnectAsync(true).ConfigureAwait(false);
}

因此,我在此处中阅读了onMessageSent,或者可以使用MessageSent函数看看是否有响应-我真的很想看一个示例代码,如何在代码中使用这些函数来确定是否确实收到了消息?

So, I read in here the onMessageSent, or MessageSent functions can be used to see if there was a response - I'd really like to see an example of code though, how would those functions be used in code to determine if the message was really received?

我确实具有包含异步发送功能的功能,该功能作为 public void ,并且警告抑制消除了VisualStudio关于未等待呼叫的投诉.

I do have the function which contains the async sending function as a public void, and the warning suppression quells the VisualStudio complaints about the call not being awaited.

public void SendEmail(string HtmlEmailContents, string SubjectLine, string CustomFromAddress = null, string CustomEmailRecipients = null)
{
  string To = getRecipientString(mainRecipients);
  string Cc = getRecipientString(ccRecipients);
  string Bcc = getRecipientString(bccRecipients);
  if(CustomEmailRecipients != null && CustomEmailRecipients != "")
  {
    To = CustomEmailRecipients;
    Cc = "";
    Bcc = "";
  }
  string finalizedFromAddress;
  if(CustomFromAddress != null)
  {
    finalizedFromAddress = CustomFromAddress;
  }
  else
  {
    finalizedFromAddress = FromAddress;
  }
  #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
  MessageServices.SendEmailAsync(
    To,
    finalizedFromAddress,
    Cc,
    Bcc,
    SubjectLine,
    HtmlEmailContents
  );
  #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}

[New Edit]:所以,让我们想象一下,我整理了整个异步内容,现在是时候真正捕获那些错误消息了.在这里,我读到可以使用MessageSent和OnMessageSent函数查看结果.当然,我无法弄清楚.我正在寻找",以获取可以使用MailKit进行镜像的一些示例.在那里,行 client.SendCompleted + = new SendCompletedEventHandler(SendCompletedCallback); 似乎握住了密钥,我想知道是否在我的代码中使用 client.MessageSent + = ??? 我们在MailKit中的对应对象.

[New Edit]: So, let's imagine I straightened out the whole async thing, and now it's time to really catch those faulty messages. Here, I have read that the MessageSent and OnMessageSent functions can be used to see the results. And of course, I can't figure this out. I am looking here for some examples which may be mirrored using the MailKit. In there, the line client.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback); seems to hold the key, and I wonder if in my code, using client.MessageSent += ??? us the counterpart inside MailKit.

推荐答案

@mason似乎在回答有关异步的滥用的原始问题.

The original question seems to be answered by @mason regarding the misuse of async.

现在就回答您的新问题.

So now to answer your new question.

MessageSent 事件与.NET中的任何其他事件一样,可以通过以下方式收听:

The MessageSent event is just like any other event in .NET and can be listened to in the following way:

client.MessageSent += OnMessageSent;

其中 OnMessageSent 方法是这样的:

void OnMessageSent (object sender, MessageSentEventArgs e)
{
    Console.WriteLine ("The message was sent!");
}

但是,您想收听此事件的原因似乎是对它真正为您提供的信息的误解.

However, the reason you are wanting to listen for this event seems to be a misunderstanding of what information it really provides you with.

是的, MessageSentEventArgs.Response 属性包含发送的实际响应通过服务器,不太可能告诉您收件人电子邮件地址是否实际存在.

While, yes, the MessageSentEventArgs.Response property contains the actual response sent by the server, it is unlikely to tell you whether or not the recipient email address(es) actually exist or not.

如果您将邮件发送到不存在的电子邮件地址,并且 SmtpClient.Send() SendAsync()不会引发异常,则意味着SMTP服务器在收到MailKit发送的 RCPT TO 命令时可能不验证电子邮件地址是否存在,并且将很乐意接受没有错误的邮件提交,这意味着MailKit将不会引发任何异常.许多SMTP服务器这样做有两个原因:

If you are sending a message to a non-existent email address and SmtpClient.Send() or SendAsync() does not throw an exception, then it means that the SMTP server likely is not verifying whether the email addresses exist when it receives the RCPT TO commands sent by MailKit and will happily accept the message submission w/o error which means no exception will be thrown by MailKit. A lot of SMTP servers do this for 2 reasons:

  1. 保护用户的匿名性(因此,垃圾邮件发送者不能使用暴力手段来弄清用户的帐户名-出于相同原因,男人禁用了 VRFY EXPN 命令).
  2. 电子邮件地址的懒惰查找-即SMTP服务器实际上不会查找电子邮件地址的存在,直到它继续将邮件转发到适当的域为止.

例如,如果您连接到smtp.gmail.com以向另一域上的用户发送消息,则smtp.gmail.com无法知道user@another-domain.com不存在直到它实际上尝试将消息转发到例如smtp.another-domain.com.

For example, if you connect to smtp.gmail.com to send a message to a user on another domain, then there's no way for smtp.gmail.com to know that user@another-domain.com doesn't exist until it actually attempts to forward the message on to e.g. smtp.another-domain.com.

如果您实际上想获得有关电子邮件地址是否实际存在的反馈,则此过程将需要您付出更多的努力和一些运气.

If you actually want to get feedback as to whether an email address actually exists or not, the process will involve a bit more effort on your part and some luck.

首先,您需要希望并祈祷您的SMTP服务器支持 DSN (投放状态通知)扩展名.

First, you'll need to hope and pray that your SMTP server supports the DSN (Delivery Status Notification) extension.

要检查服务器是否支持此功能,可以检查 SmtpClient.Capabilities :

To check if your server supports this, you can check SmtpClient.Capabilities:

if (client.Capabilities.HasFlag (SmtpCapability.Dsn)) {
    ...
}

努力.

假设您的服务器支持DSN扩展,接下来您需要对SmtpClient进行子类化,以便您可以重写某些方法,以便为MailKit的SmtpClient提供一些所需的信息/选项.

The Effort.

Assuming your server supports the DSN extension, next you'll need to subclass SmtpClient so that you can override some methods in order to provide MailKit's SmtpClient with some needed information/options.

这些方法是:

  1. GetDeliveryStatusNotifications
  2. GetEnvelopeId

这两种方法的文档已经提供了以下代码片段,但为了后代,我将其粘贴在这里:

The documentation for both methods already provides the following code-snippet, but I'll paste it here for posterity:

public class DSNSmtpClient : SmtpClient
{
    public DSNSmtpClient ()
    {
    }

    /// <summary>
    /// Get the envelope identifier to be used with delivery status notifications.
    /// </summary>
    /// <remarks>
    /// <para>The envelope identifier, if non-empty, is useful in determining which message
    /// a delivery status notification was issued for.</para>
    /// <para>The envelope identifier should be unique and may be up to 100 characters in
    /// length, but must consist only of printable ASCII characters and no white space.</para>
    /// <para>For more information, see rfc3461, section 4.4.</para>
    /// </remarks>
    /// <returns>The envelope identifier.</returns>
    /// <param name="message">The message.</param>
    protected override string GetEnvelopeId (MimeMessage message)
    {
        // Since you will want to be able to map whatever identifier you return here to the
        // message, the obvious identifier to use is probably the Message-Id value.
        return message.MessageId;
    }

    /// <summary>
    /// Get the types of delivery status notification desired for the specified recipient mailbox.
    /// </summary>
    /// <remarks>
    /// Gets the types of delivery status notification desired for the specified recipient mailbox.
    /// </remarks>
    /// <returns>The desired delivery status notification type.</returns>
    /// <param name="message">The message being sent.</param>
    /// <param name="mailbox">The mailbox.</param>
    protected override DeliveryStatusNotification? GetDeliveryStatusNotifications (MimeMessage message, MailboxAddress mailbox)
    {
        // In this example, we only want to be notified of failures to deliver to a mailbox.
        // If you also want to be notified of delays or successful deliveries, simply bitwise-or
        // whatever combination of flags you want to be notified about.
        return DeliveryStatusNotification.Failure;
    }
}

好的,现在您已经完成了上述操作……如果服务器无法将邮件传递给任何收件人,这将要求SMTP服务器向您发送电子邮件.

Okay, now that you've done the above... this will request that the SMTP server sends you an email if/when the server fails to deliver the message to any of the recipients.

现在,您可以处理收到的所述电子邮件...

Now you get to handle receiving said emails...

当您收到这些消息之一时,它将具有 multipart/report的顶级 Content-Type ;report-type ="delivery-status" ,它将由 MultipartReport

When you get one of these messages, it will have a top-level Content-Type of multipart/report; report-type="delivery-status" which will be represented by a MultipartReport

检测到此问题的方法是:

The way to detect this is:

var report = message.Body as MultipartReport;
if (report != null && report.ReportType != null && report.ReportType.Equals ("delivery-status", StringComparison.OrdinalIgnoreCase)) {
    ...
}

然后,您需要做的是找到 Content-Type message/delivery-status 且属于子代的MIME部分.> multipart/report (每个都将由 MessageDeliveryStatus 表示)):

Then what you will need to do is locate the MIME part(s) with a Content-Type of message/delivery-status that are children of the multipart/report (each of which will be represented by MessageDeliveryStatus):

foreach (var mds in report.OfType<MessageDeliveryStatus> ()) {
    ...
}

然后,您需要检查 StatusGroups 以便提取您需要的信息. StatusGroups 属性是 HeaderListCollection ,它本质上是键值对列表的列表.

Then you'll need to check the StatusGroups in order to extract the information you need. The StatusGroups property is a HeaderListCollection which is essentially a list of a list of key-value pairs.

要弄清楚可用的密钥,您需要通读第2.2节第2.3 部分.ietf.org/html/rfc3464"rel =" noreferrer> rfc3464 .

To figure out what keys are available, you'll need to read over Section 2.2 and Section 2.3 of rfc3464.

至少,您需要检查第一个StatusGroup中的"Original-Envelope-Id" ,以便弄清楚该报告所针对的消息(此信封ID字符串将与之匹配您在 GetEnvelopeId 中返回的字符串).

At a minimum, you'll need to check the "Original-Envelope-Id" in the first StatusGroup in order to figure out which message the report is for (this envelope id string will match the string you returned in GetEnvelopeId).

var envelopeId = mds.StatusGroups[0]["Original-Envelope-Id"];

在以下每个StatusGroups中,您需要获取原始收件人" 的值(如果已设置,否则我想您可以检查"Final-Recipient").格式为 rfc822; user@domain.com -因此,只需将';'字符分割并使用第二个字符串即可.

In each of the following StatusGroups, you'll want to get the value for the "Original-Recipient" (if set, otherwise I guess you could check the "Final-Recipient"). This will be of the form rfc822;user@domain.com - so just split on the ';' character and use the second string.

最后,您将需要检查"Action" 值,以确定接收者的状态是什么.对于您而言,如果值为"failed" ,则表示传送失败.

And finally you'll want to check the "Action" value to figure out what the status of said recipient is. In your case, if the value is "failed", then it means that delivery failed.

for (int i = 1; i < mds.StatusGroups.Length; i++) {
    var recipient = mds.StatusGroups[i]["Original-Recipient"];
    var action = mds.StatusGroups[i]["Action"];

    if (recipient == null)
        recipient = mds.StatusGroups[i]["Final-Recipient"];

    var values = recipient.Split (';');
    var emailAddress = values[1];

    ...
}

如果将所有内容放在一起,就会得到以下内容:

If you put it all together, you get something like this:

public void ProcessDeliveryStatusNotification (MimeMessage message)
{
    var report = message.Body as MultipartReport;

    if (report == null || report.ReportType == null || !report.ReportType.Equals ("delivery-status", StringComparison.OrdinalIgnoreCase)) {
        // this is not a delivery status notification message...
        return;
    }

    // process the report
    foreach (var mds in report.OfType<MessageDeliveryStatus> ()) {
        // process the status groups - each status group represents a different recipient

        // The first status group contains information about the message
        var envelopeId = mds.StatusGroups[0]["Original-Envelope-Id"];

        // all of the other status groups contain per-recipient information
        for (int i = 1; i < mds.StatusGroups.Length; i++) {
            var recipient = mds.StatusGroups[i]["Original-Recipient"];
            var action = mds.StatusGroups[i]["Action"];

            if (recipient == null)
                recipient = mds.StatusGroups[i]["Final-Recipient"];

            // the recipient string should be in the form: "rfc822;user@domain.com"
            var index = recipient.IndexOf (';');
            var address = recipient.Substring (index + 1);

            switch (action) {
            case "failed":
                Console.WriteLine ("Delivery of message {0} failed for {1}", envelopeId, address);
                break;
            case "delayed":
                Console.WriteLine ("Delivery of message {0} has been delayed for {1}", envelopeId, address);
                break;
            case "delivered":
                Console.WriteLine ("Delivery of message {0} has been delivered to {1}", envelopeId, address);
                break;
            case "relayed":
                Console.WriteLine ("Delivery of message {0} has been relayed for {1}", envelopeId, address);
                break;
            case "expanded":
                Console.WriteLine ("Delivery of message {0} has been delivered to {1} and relayed to the the expanded recipients", envelopeId, address);
                break;
            }
        }
    }
}

这篇关于ASP.NET MailKit SMTP响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆