.NET-es kódból e-mailt küldeni nem éppen atomfizikusnak való feladat, mindenki hamar rájön, hogy milyen egyszerű spam gyárat írni a System.Net.Mail névtér segítségével:
MailMessage msg = new MailMessage( "felado@example.com", "cimzett@example.com", "Próba levél tárgysora kódból", "Ide jön az érdekes tartalom..." ); SmtpClient smtp = new SmtpClient( "mail.example.com" ); smtp.Send( msg );
Ez eddig ment .NET 1.0-n is, ami viszont macerás volt, az a feladó és a címzettek megadása, amire a megoldást a .NET 2.0-ban megjelent MailAddress osztály hozta meg. Így már barátságosabban címezhetünk levelet:
MailMessage msg = new MailMessage(); msg.From = new MailAddress( "felado@example.com", "Feladó szép neve" ); msg.To.Add( new MailAddress( "cimzett@example.com", "Címzett szép neve" ) ); msg.Subject = "Próba levél tárgysora"; msg.Body = "A tartalom helye...";
Ez mind szép, de a címzettel kár sokat erőlködni, egy Exchange Serverre kötött Outlook ragaszkodik az AD-ben lévő Display Name-hez. Egyébként pedig a webes levelező kliensek közül egyik-másik (pl. a Freemail biztosan) nem tud megbírkózni a címmezőkben lévő ékezetekkel, hiába adunk meg a MailAddress konstruktornak harmadik paraméterben egy Encodingot. Érdekes módon a Gmailnek meg se kottyan…
Ha már a címzetteknél járunk, bizony mindenkinek akadnak olyan levelezőpartnerei, akiknél nem ártana a kézbesítési és az olvasási nyugta használata. Örüljünk, mert kézbesítési nyugtát kérhetünk a DeliveryNotificationOptions tulajdonság használatával. Mivel ez flages enum bátran összevagyolhatjuk az egyes elemeit, például:
msg.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure | DeliveryNotificationOptions.OnSuccess;
Az olvasási nyugtára viszont nincs közvetlenül property, így nincs más lehetőségünk, mint kézzel beletúrni a fejléc mezőkbe:
msg.Headers.Add( "Disposition-Notification-To", "felado@example.com" );
Ennyit a címzésről, lépjünk tovább a tartalomra, például legyen inkább HTML:
msg.Body = "<html><body><h1>Hello Szivi!</h1>Hoztál <i>nekem</i> kismajmot?</body></html>"; msg.IsBodyHtml = true;
Na ezt már nem sikerült elküldeni az Exchange-en keresztül:
Unhandled Exception: System.Net.Mail.SmtpException: Mailbox unavailable. The server response was: 5.7.1 Requested action not taken: message refused
A finnyás mindenit, túl spam gyanúsnak találta a levelet, ezért kénytelen voltam a lokális SMTP szerveremen keresztül elküldeni. Mert XP-n még van olyan, Vistán nincs, de majd a Windows Server 2008-ban lesz.
Az AlternateView osztály segítségével még arra is van lehetőségünk, hogy egyetlen levélben különböző formátumokban küldjük el a mondandónkat.
string htmlBody = "<html><body><h1>Hello Szivi!</h1>Hoztál <i>nekem</i> kismajmot?</body></html>"; string textBody = "Szia Szivi! Hoztál nekem kismajmot?"; AlternateView htmlView = AlternateView.CreateAlternateViewFromString( htmlBody, Encoding.Default, MediaTypeNames.Text.Html ); msg.AlternateViews.Add( htmlView ); AlternateView textView = AlternateView.CreateAlternateViewFromString( textBody, Encoding.Default, MediaTypeNames.Text.Plain ); msg.AlternateViews.Add( textView );
Ez (legalább) két ok miatt érdekes:
- Az AlternateView osztálynak van olyan konstruktora, amelyet fájl név vagy stream megadásával használhatunk, ha tehát nem változik a szöveg, akkor egyszerűen felnyalhatjuk fájlrendszerből.
- Így van lehetőségünk arra, hogy a HTML változathoz – és csak ahhoz – képet fűzzünk. Nosza!
string htmlBody = @"<html><body><h1>Képes</h1><img src=""cid:Kep1""></body></html>"; AlternateView htmlView = AlternateView.CreateAlternateViewFromString( htmlBody, Encoding.Default, MediaTypeNames.Text.Html ); LinkedResource pic = new LinkedResource( @"C:\WINDOWS\Web\Wallpaper\Crystal.jpg", MediaTypeNames.Image.Jpeg ); pic.ContentId = "Kep1"; htmlView.LinkedResources.Add( pic ); msg.AlternateViews.Add( htmlView );
Aláhúztam a trükkös részeket, a ContentId tulajdonságra kell figyelni, és az IMG elem SRC attribútumában cid:ContentId formátumban hivatkozhatunk a képre.
Így már persze jó nagy leveleket fogunk küldözgetni, nem is fog gyorsan menni. Küldjük hát aszinkron módon, mert 2.0-ban már azt is lehet:
smtp.SendCompleted += new SendCompletedEventHandler( OnSendCompleted ); smtp.SendAsync( msg, "Levél a Kedvesnek" );
A küldés eredményét pedig egy eseménykezelővel tudhatjuk meg:
static void OnSendCompleted( object sender, AsyncCompletedEventArgs e ) { string state = e.UserState as string; if( e.Cancelled ) { Console.WriteLine( @"""{0}"" küldése megszakítva.", state ); } if( e.Error != null ) { Console.WriteLine( @"Hiba a ""{0}"" küldése közben: {1}", state, e.Error.Message ); } else { Console.WriteLine( @"A ""{0}"" elküldve.", state ); } }
A teljességhez hozzátartozik, hogy az SmtpClient.SendAsyncCancel() metódus hívásával leállíthatjuk a levél elküldését. Ezt jelenlegi formájában a valóságban aligha fogjuk használni, hiszen nem tudunk hivatkozni egy konkrét MailMessage objektumra.
Ha valami miatt mégis ragaszkodnánk a szinkron küldéshez, ne feledkezzünk meg a Timeout tulajdonságról, ami alapértelmezés szerint 100 másodperc, addig próbálkozik a küldéssel. Apropó, ha már a küldésnél tartunk, igen örvendetes, hogy már van lehetőségünk a kapcsolat titkosítására (SmtpClient.EnableSsl) és hitelesítésre is (SmtpClient.Credentials).
Aki pedig arra kíváncsi, hogy mi történik a háttérben, ne habozzon felhúzni néhány trace listenert az app.config fájlban, egészen részletes olvasnivalóban lehet része:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <trace autoflush="true" /> <sources> <source name="System.Net" > <listeners> <add name="MyTraceFile"/> </listeners> </source> <source name="System.Net.Sockets"> <listeners> <add name="MyTraceFile"/> </listeners> </source> </sources> <sharedListeners> <add name="MyTraceFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="System.Net.trace.log" /> </sharedListeners> <switches> <add name="System.Net" value="Verbose" /> <add name="System.Net.Sockets" value="Verbose" /> </switches> </system.diagnostics> </configuration>
És hogy valami stílszerű maradjon a végére, ezt soha ne felejtsük el:
msg.Dispose();
Köszi a példát!
Van azonban egy problémám, hogy tudok levelet küldeni, ha az smtp szerver felhasználó nevet és jelszót kér a küldéshez.
Ha ezzel kapcsolatban tudnál némi útmutatással szolgálni azért hálás lennék!Üdv! Mato
Az SmtpClient példánynak van egy Credentials nevű property-je
client.Credentials = new NetworkCredential(username, password); //(System.Net névtérből)
Pingback: E-mail küldés kódból – sablonnal « Balássy György szakmai blogja