E-mail küldés kódból

.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:

  1. 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.
  2. Í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();


Balássy György (MS RD, ASP.NET MVP, MCTS)

Balássy György (MS RD, ASP.NET MVP, MCTS) Villamosmérnök, a BME Automatizálási és Alkalmazott Informatikai Tanszékén webportálok fejlesztését oktatja. 2000 óta foglalkozik a Microsoft .NET platformjával, melynek meghonosításában jelentős szerepet vállalt előadóként, konzulensként és A .NET Framework és programozása című könyv társszerzőjeként. Az MSDN Kompetencia Központon belül a Portál Technológiák Csoport vezetője, szakterülete web alapú rendszerek fejlesztése és üzemeltetése. 2004-ben Magyarországon elsőként kapta meg a Most Valuable Professional címet, majd 2005 óta a Microsoft magyarországi regionális igazgatója. Publikációi a Technet Magazinban, az MSDN Kompetencia Központ honlapján és szakmai blogjában olvashatóak.

2007.08.28. 20:24:00 | Permalink | Hozzászólások: 0 | Tárgyszavak: ,


  • OBA "state of the art": addin, ribbon, task pane - Site Source

    Balássy György (MS RD, ASP.NET MVP, MCTS) Múlt héten egy barátom jóvoltából (ezúton is köszönet érte) kezembe akadt a 6 Microsoft® Office Business Applications for Office SharePoint® Server 2007 című MS Press könyv. Bevallom elsőre vegyes érzelmeim voltak a könyvvel kapcsolatban, mert bár a SharePoint programozás érdekel, az "OBA" hallatán feláll a szőr a hátamon. Tovább »
  • ASP.NET 4: fogyókúrán a web.config

    Balássy György (MS RD, ASP.NET MVP, MCTS) Aki hosszabb ideje foglalkozik már az ASP.NET-tel, annak biztosan feltűnt, hogy minden verzióval jelentősen hízott a web.config. Elég csak létrehozni egy új webhelyet Visual Studioban és kapunk egy több képernyős konfig fájlt, ami ráadásul szinte minden webalkalmazásnál ugyanaz. A beállítások többségéhez többnyire hozzá sem nyúlunk (tegye fel a kezét, aki kiszedte a .vb fájlok fordítására vonatkozó beállításokat egy C# projektből), viszont nap mint nap kerülgetjük. A helyzet az IIS 7-tel csak “rosszabbodott”, hiszen Windows Server 2008-on már a webkiszolgáló beállításai is a web.configba kerülnek. Tovább »


Írja meg Ön is véleményét!


Hozzászólásokat csak regisztrált, bejelentkezett felhasználóktól tudunk elfogadni!

Hozzászólások