Klasy: MailMessage, MailAddress, MailAddressCollection, SmtpClient, SmtpPermission, SmtpPermissionAttribute, Attachment, AttachmentBase, AttachmentCollection, SmtpException, SmtpFailedReceipientException, SmtpFailedReceipientsException, LinkedResource, LinkedResourceCollection, AlternateView, AlternateViewCollection. Delegat SendCompletedEventHandler.
Klasa MailMessage (z System.Net.Mail) reprezentuje wiadomość e-mail, która może być wysłana przez użycie klasy SmtpClient (istnieje także możliwość użycia przeładowanej metody SmtpClient.Send do wysłania maila bez tworzenia obiektu MailMessage). Oprócz bezparametrowego konstruktora mamy jeszcze 3 do dyspozycji:
- public MailMessage(MailAddress from, MailAddress to),
- public MailMessage(string from, string to),
- public MailMessage(string from, string to, string subject, string body).
Klasa ma oczywiste własności:
- public MailAddress From { get; set; },
- public MailAddressCollection To { get; },
- public MailAddressCollection CC { get; } /Carbon Copy/,
- public MailAddressCollection Bcc { get; } /Blind Carbon Copy/,
- public AttachmentCollection Attachments { get; },
- public string Subject { get; set; },
- public string Body { get; set; } /Body ContentType to text/plain/,
- public Encoding BodyEncoding { get; set; } /Domyślnie temat i zawartość używają kodowania bazując na ustawieniach lokalnych/,
- public Encoding SubjectEncoding { get; set; } /gdy agent pocztowy ma problem z Unicode, to ustaw na UTF8/,
- public AlternateViewCollection AlternateViews { get; } /umożliwia wysyłkę w formacie HTML i tekstowym/,
Rzadziej są używane:
- public DeliveryNotificationOptions DeliveryNotificationOptions { get; set; },
- public NameValueCollection Headers { get; },
- public bool IsBodyHtml { get; set; } /domyślnie false/,
- public MailPriority Priority { get; set; },
- public MailAddress ReplyTo { get; set; },
- public MailAddress Sender { get; set; }.
Zamiast korzystać z Bcc powinieneś/aś wysłać oddzielne kopie wiadomości do każdego odbiorcy - filtry spamowe często blokują wiadomości, które nie maja adresu odbiorcy w nagłówku To.
Skoro mamy utworzoną wiadomość, to wypadałoby ją wysłać. Klasą reprezentującą serwer SMTP jest SmtpClient. Najbardziej rozbudowane przeładowanie konstruktora umożliwia określenie od razu hosta (nazwa lub IP) i portu: public SmtpClient(string host, int port). Jeśli host jest = null lub String.Empty, to jest inicjalizowany z użyciem plików konfiguracyjnych aplikacji lub maszyny. Analogicznie dla własności Port i Credentials. Gdy określisz tą informację w kodzie, to pliki nie są brane pod uwagę:
<configuration>
<system.net>
<mailSettings>
<smtp deliveryMethod="network">
<network host="localhost" port="25" defaultCredentials="true"/>
</smtp>
</mailSettings>
</system.net>
</configuration>
Własności:
- public X509CertificateCollection ClientCertificates { get; } - określa które certyfikaty powinny być użyte do nawiązania połączenia SSL (opcjonalne),
- public ICredentialsByHost Credentials { get; set; } - używane do uwierzytelnienia nadawcy np. client.Credentials = new NetworkCredential("user", "password"); gdy określisz UseDefaultCredentials=false, to ta własność będzie użyta,
- public SmtpDeliveryMethod DeliveryMethod { get; set; } - bardzo użyteczna własność zwłaszcza podczas testów, mamy do wyboru:
- Network - przez sieć do serwera SMTP,
- SpecifiedPickupDirectory - email jest kopiowany do katalogu określonego przez własność PickupDirectoryLocation (może być później wysłany przez zewnętrzną aplikację),
- PickupDirectoryFromIis - mail jest kopiowany do katalogu używanego przez lokalnego IISa,
- public bool EnableSsl { get; set; } - powiązane z ClientCertificates,
- public string Host { get; set; },
- public string PickupDirectoryLocation { get; set; },
- public int Port { get; set; },
- public ServicePoint ServicePoint { get; } - połączenie sieciowe używane do transmisji wiadomości email,
- public int Timeout { get; set; } - domyślnie 100000 (100 sekund); wywołanie metody Send blokuje do czasu zakończenia operacji, jeśli ustawisz tą własność i nie operacja nie będzie mogła być zakończona w określonym czasie, to SmtpClient rzuci SmtpException,
- public bool UseDefaultCredentials { get; set; } - niektóre serwery SMTP wymagają, aby klient był uwierzytelniony przed wysłaniem maila.
Metody - wysyłanie synchroniczne:
- public void Send(MailMessage message),
- public void Send(string from, string recipients, string subject, string body),
Nie możesz ich wywołać, gdy wiadomość jest aktualnie wysyłana asynchronicznie. Wyjątki:
- InvalidOperationException - nie określona nazwa serwera/zerowy port,
- SmtpException z inner WebException - nie można znaleźć serwera o podanej nazwie (patrz ex.GetBaseException()),
- SmtpException - błąd uwierzytelniania, problemy z transmisją,
- SmtpFailedRecipientException - na lokalnym serwerze nie ma określonej skrzynki (dziedziczy z SmtpException),
- SmtpFailedRecipientsException - dziedziczy z SmtpFailedRecipientException (zwróć uwagę na kolejność w bloku catch - powinna być odwrotna niż tu podana), obsługuje infrastrukturę .NET Framework; gdy email nie może być dostarczony do niektórych odbiorców.
Sprawdź także własność StatusCode SmtpException - przykłady: ServiceNotAvailable, MailboxBusy, MailboxUnavailable, ale także OK, ServiceReady czy HelpMessage.
Dla wysyłki asynchronicznej mamy następujące elementy:
- public void SendAsync(MailMessage message, Object userToken) // userToken - obiekt, który jest przekazywany do metody wywoływanej kiedy operacja asynchroniczna została zakończona,
- public void SendAsync(string from, string recipients, string subject, string body, Object userToken),
- public void SendAsyncCancel(),
- public event SendCompletedEventHandler SendCompleted,
- public delegate void SendCompletedEventHandler(Object sender, AsyncCompletedEventArgs e).
Przed wywołaniem SendAsync musisz poczekać na zakończenie wysyłki poprzednich maili (inaczej dostaniesz wyjątek InvalidOperationException). Aplikacja może wykryć błąd przez badanie własności Error przekazanej do delegata SendCompletedEventHandler. Własność Timeout nie ma zastosowania dla wywołania SendAsync. W klasie AsyncCompletedEventArgs są 3 własności:
- public bool Cancelled { get; },
- public Exception Error { get; },
- public Object UserState { get; } - unikalnie identyfikuje zadanie asynchroniczne.
Klasa AttachmentBase jest abstrakcyjną klasą bazową dla AlternateView, Attachment i LinkedResource. To chyba byłoby wszystko - w końcu czas na przykład:
public static void Main()
{
MailMessage m = new MailMessage(new MailAddress("lance@contoso.com", "Lance Tucker", System.Text.Encoding.UTF8), new MailAddress("ben@contoso.com", "Ben Miller", System.Text.Encoding.UTF8)); // określanie kodowania jest rzadko potrzebne
try
{
m.From = new MailAddress("lance"); // jest pełna walidacja - nie musisz walidować regexp-ami
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
m.To.Add(new MailAddress("james@contoso.com", "James van Eaton"));
m.DeliveryNotificationOptions = DeliveryNotificationOptions.Delay; // jeśli wiadomość będzie opóźniona, to serwer SMTP wyśle wiadomość na adres określony we własności From
m.ReplyTo = new MailAddress("reply@domain.com"); // adres inny niż From będzie używany do odpowiedzi
m.Priority = MailPriority.High; // nie zmienia funkcjonowania serwera
// załączniki - najprościej:
string path = @"C:\Users\Compal\Desktop\MCTS 70-536\foldery\temp\attachments\";
m.Attachments.Add(new Attachment(Path.Combine(path, "sampleText.txt")));
// bardziej ambitnie:
Stream sr = new FileStream(path + "Hello.exe", FileMode.Open, FileAccess.Read);
Attachment myAttachment = new Attachment(sr, "hello.exe", MediaTypeNames.Application.Octet); // określ nazwę, bo załącznik będzie się nazywał np. application_octet-stream.dat
ContentDisposition disposition = myAttachment.ContentDisposition; // MIME content disposition
disposition.ReadDate = System.IO.File.GetLastAccessTime(path + "Hello.exe");
m.Attachments.Add(myAttachment);
m.Subject = "Quarterly data report."; // zawsze w plain text
// wbudowanie obrazków w wiadomość HTML (odniesienie się przez content ID):
m.IsBodyHtml = true;
string htmlBody = "<html><body><h1>My Message</h1><br>This is an HTML msg.<br><img src=\"cid:Pic1\"></body></html>"; // większość klientów pocztowych ignoruje sekcję <head>, a także skrypt po stronie klienta oraz nie ściąga automatycznie obrazków z sieci
AlternateView avHtml = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
LinkedResource pic1 = new LinkedResource(path + "parking.jpg", MediaTypeNames.Image.Jpeg);
pic1.ContentId = "Pic1"; // odpowiada cid:Pic1
avHtml.LinkedResources.Add(pic1);
string textBody = "You must use an e-mail client that supports HTML messages";
AlternateView avText = AlternateView.CreateAlternateViewFromString(textBody, null, MediaTypeNames.Text.Plain);
m.AlternateViews.Add(avHtml);
m.AlternateViews.Add(avText);
// -----------------------------------
SmtpClient client = new SmtpClient("127.0.0.1");
// client.Credentials = (ICredentialsByHost)CredentialCache.DefaultNetworkCredentials;
client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = path;
client.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
string userState = "message1";
client.SendAsync(m, userState); // dowolny obiekt, który pozwala metodzie callback na identyfikację tej operacji
Console.WriteLine("Sending message... press c to cancel mail. Press any other key to continue.");
string answer = Console.ReadLine();
if (answer.StartsWith("c") && mailSent == false)
client.SendAsyncCancel();
// jeszcze raz - synchronicznie:
try
{
client.Send(m);
}
catch (SmtpFailedRecipientsException ex)
{
for (int i = 0; i < ex.InnerExceptions.Length; i++)
{
SmtpStatusCode status = ex.InnerExceptions[i].StatusCode;
if (status == SmtpStatusCode.MailboxBusy || status == SmtpStatusCode.MailboxUnavailable)
{
Console.WriteLine("Delivery failed - retrying in 5 seconds.");
System.Threading.Thread.Sleep(5000);
client.Send(m);
}
else
Console.WriteLine("Failed to deliver message to {0}", ex.InnerExceptions[i].FailedRecipient); // tablica SmtpFailedRecipientException
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
m.Dispose();
Console.WriteLine("Goodbye.");
}
}
static bool mailSent = false;
private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
String token = (string)e.UserState;
if (e.Cancelled)
Console.WriteLine("[{0}] Send canceled.", token);
if (e.Error != null)
Console.WriteLine("[{0}] {1}", token, e.Error.ToString());
else
Console.WriteLine("Message sent.");
mailSent = true;
}
Na koniec zrzut ekranu:
