次の方法で共有


Handling Exceptions when Sending Mail via System.Net.Mail

I am sure alot of us when sending mail from a web application, we sometimes run into some unforeseen error becuase the mail being sent threw an exception. How do we solve this? Do we just throw the exception and that's it. No, If we look into this problem a little closer, the majority of mail server interruptions are very temporary, only lasting a few seconds. Instead of throwing the exception right away, why not try sending the mail again?

If there is a problem of any kind at the SMTP server level, the server reports an exception, you will probally use SmtpException to handle this. While using the SmtpException works well, its functionality is limited in determining exactly how the send failed.

Why do we care? Well, sometimes our mail server is experiencing a serious issue, and most likely, there’s nothing you can do about it. But, when a SMTP server reports an error, the actual problem is not with the server itself but with an individual mailbox. The most common cause of mailbox issues are as follows:

  1. The destination mailbox is currently in use. If this occurs, only lasts a short amount of time.
  2. The mailbox is unavailable. This may mean that there is actually no such address on the server, but it may also mean that the search for the mailbox simply timed out.
  3. The transaction with the mailbox failed. The reasons for this can be somewhat mysterious; but like a stubborn web page, sometimes a second nudge is all it takes to do the trick.

What does this tell us? With a little more coding, we could gracefully recover from the majority of email send failures by detecting these particular conditions and attempting the send a second time.

Remember, if a second mail send happens to fail, then it’s very likely that the problem is not temporary, in which case there is not reason continuing to handle the exception at this point. When that happens, we can allow the exception to bubble up to the calling page as it normally would.

Handling the Exceptions

Problems with mailboxes is that they are not easily discovered by catching SmtpException. Fortunately, there is a more derived type called SmtpFailedRecipientException that the .NET Framework uses to wrap errors reported from an individual mailbox. This exception contains a StatusCode property of type enum that will tell us the exact cause of the error.

Obvserve in the following code below:

 using System.Net.Mail;

using System.Threading;

using System.Web.Configuration;



/// <summary>

/// Provides a method for sending email.

/// </summary>

public static class Email

{

    /// <summary>

    /// Constructs and sends an email message.

    /// </summary>
/// <param name="fromName">The display name of the person the email is from.</param>
/// <param name="fromEmail">The email address of the person the email is from.</param>
/// <param name="subject">The subject of the email.</param>
/// <param name="body">The body of the email.</param>
public static void Send(string fromName, string fromEmail, string subject, string body)

    {
MailMessage message = new MailMessage

        {
IsBodyHtml = false,
From = new MailAddress(fromEmail, fromName),

            Subject = subject,

            Body = body

        };
message.To.Add(WebConfigurationManager.AppSettings["mailToAddress"]);



        Send(message);

    }


private static void Send(MailMessage message)

    {
SmtpClient client = new SmtpClient();



        try

        {

            client.Send(message);

        }
catch (SmtpFailedRecipientException ex)

        {
SmtpStatusCode statusCode = ex.StatusCode;


if (statusCode == SmtpStatusCode.MailboxBusy ||
statusCode == SmtpStatusCode.MailboxUnavailable ||
statusCode == SmtpStatusCode.TransactionFailed)

            {

                // wait 5 seconds, try a second time

                Thread.Sleep(5000);

                client.Send(message);

            }

            else

            {

                throw;

            }

        }

        finally

        {

            message.Dispose();

        }

    }

}

As you can see in the above code block, we’re catching the SmtpFailedRecipientException that is thrown as a result of the mailbox error, and examining its StatusCodebefore actually handling the exception. If the reported error is due to the mailbox being busy or unavailable, or the transaction fails, we are using the Thread.Sleep(5000) method to wait five seconds before trying the send the mail message again. If the error is caused by any other reason, the exception is passed unhandled back to the caller, and subsequently back to the page, where it will be handled as its base type. A failure on the second send will also cause the exception to propagate back to the page.

Hope this helps... Happy Coding!