Thursday, August 2, 2012

[C#] Using SmtpClient sent email with response via Gmail

最近用Sikuli做了個UI Test,要把結果用email的方式丟出來,找了一下,原本打算做模組化一點,各個不同的測試從Sikuli回傳給Python然後再去處理寄信的問題,後來討論了一下,多花力氣去維護一個語言不太划算(雖然我想玩Python啊),所以決定自己寫個頁面去做寄信的事。

找了一下,.NET有這個SmtpClient 類別可以來寄信,而且sample code也蠻詳細的,所以就來試一下了。

我是直接用需要帳號密碼登入才能用的gmail來寄,一開始的code是長這樣
    private const string Username = "username"; //update your gmail acc/pw here
    private const string Passowrd = "passowrd";

    private string MailBody {
        get
        {
            return string.IsNullOrEmpty(Request.QueryString["result"])
                       ? string.Empty
                       : Request.QueryString["result"];
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (MailBody == string.Empty) return;

        using(var smtpServer = new SmtpClient("smtp.gmail.com", 587)
                             {
                                 EnableSsl = true,
                                 Timeout = 10000,
                                 DeliveryMethod = SmtpDeliveryMethod.Network,
                                 Credentials = new NetworkCredential(Username, Passowrd)
                             })
        {
            smtpServer.Send(new MailMessage("[email protected]", "[email protected]", "test", MailBody)
                                {
                                    BodyEncoding = Encoding.UTF8,
                                    DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure
                                });
        }
   }

其實這樣就可以寄了,但是拿不到成功或失敗的訊息,所以後來又加了SmtpClient.SendCompleted Event來去抓回應,這邊除了加上SendCompleted Event,寄信時原本是用.Send也要改成SendAsync,然後因為改成非同步模式,所以如果你在webpage上做的話,必須在 .aspx 的 <%@ Page %> 內加上 Async="true" ,不然會有錯誤訊息的。

原本以為這樣就可以拉到寄信的結果,結果我發現一直都是回應 Cancelled ,研究了很久,最後發現因為前面是使用 using 去叫 SmtpClient ,所以他事情做完就自動去 Dispose(),但是現在改用非同步模式去寄信,所以一Send SmtpClient 就被清掉,自然是不會有結果的(我猜他Dispose()時有可能自動做了SendAsyncCancel()還是因為物件不見就怎樣的關係),最後我改了寫法,長的像下面這樣。

    private const string Username = "username"; //update your gmail acc/pw here
    private const string Passowrd = "passowrd";

    private string MailBody {
        get
        {
            return string.IsNullOrEmpty(Request.QueryString["result"])
                       ? string.Empty
                       : Request.QueryString["result"];
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (MailBody == string.Empty) return;

        var smtpServer = new SmtpClient("smtp.gmail.com", 587)
                             {
                                 EnableSsl = true,
                                 Timeout = 10000,
                                 DeliveryMethod = SmtpDeliveryMethod.Network,
                                 Credentials = new NetworkCredential(Username, Passowrd)
                             };

        var message = new MailMessage("[email protected]", "[email protected]", "test", MailBody)
                          {
                              BodyEncoding = Encoding.UTF8,
                              DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure
                          };

        smtpServer.SendCompleted += SendCompletedCallback;
        try
        {
            smtpServer.SendAsync(message, "token");
        }
        catch (Exception ex)
        {
            Label1.Text = ex.Message;
        }
    }

    private void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
    {
        // Get the unique identifier for this asynchronous operation.
        var token = (string)e.UserState;

        if (e.Cancelled)
        {
            Label1.Text = String.Format("[{0}] Send canceled.", token);
            return;
        }
        Label1.Text = e.Error != null ? String.Format("[{0}] {1}", token, e.Error) : String.Format("Message [{0}] sent.", token);
    }

使用SendAsync寄的時候,因為非同步的關係,所以會多要你傳一個token來辨識這是哪封信的結果,這在寄多封信的時候很有用,我這邊只寄一封信我就不理他了,詳情可以參閱SmtpClient.SendAsync 方法 (MailMessage, Object)

基本上這樣就可以成功的用Gmail去幫你寄信了,這在自己做測試或監控,需要小量寄信的時候很方便,當然這段程式再延展起來也是可以拿來寄電子報、會員信之類的,就看你怎麼發揮了。

SmtpClient

最後附上寄成功的信件內容,收工。

歐對了,我這個頁面的需求是會把QueryString "result"的內容寄出去,所以會多一小段東西,不然會更簡潔一點。

No comments:

Post a Comment