邊做邊學 Web Application Toolkit 系列 6 - Web Application Toolkit for Template Driven Email 程式開發與解析

邊做邊學 Web Application Toolkit for Template Driven Email 程式開發與解析


其實筆者認為發送 E-Mail 程式碼並非是本 Toolkit 的重點,因為大家過去幾乎都有撰寫 E-Mail 發送程式經驗,所以這算不上什麼難事;反倒是 XML 套用 XSL 的程式設計才是本 Toolkit 重心,弄懂了 XSL 原理,自然整個程式邏輯都能輕易看懂。故本篇將從 XSL 設計與運用原理切入,再講解 Email.cs 及 EmailTemplate.cs 元件設計手法,相信您可以很快就貫通整個 Toolkit 程式脈絡。

一.XML 套用 XSL 觀念解析

在此我們先來練習 XML 如何套用 XSL 樣板樣,以下是步驟說明:

Step 1:建立 XML 檔

首先建立一個出版社的書籍銷售 Sales.xml 檔:

<?xml version="1.0" encoding="utf-8" ?>
<sales>
  <summary>
    <heading>愛書之友出版社</heading>
    <subhead>全省銷售報告</subhead>
    <description>台北,台中,高雄區域銷售報告</description>
  </summary>

  <data>
    <region>
      <name>台北</name>
      <quarter number="1" books_sold="24000" />
      <quarter number="2" books_sold="38600" />
      <quarter number="3" books_sold="44030" />
      <quarter number="4" books_sold="21000" />
    </region>
    <region>
      <name>台中</name>
      <quarter number="1" books_sold="11000" />
      <quarter number="2" books_sold="16080" />
      <quarter number="3" books_sold="25000" />
      <quarter number="4" books_sold="29000" />
    </region>
    <region>
      <name>高雄</name>
      <quarter number="1" books_sold="27000" />
      <quarter number="2" books_sold="31400" />
      <quarter number="3" books_sold="40100" />
      <quarter number="4" books_sold="30000" />
    </region>
  </data>
</sales>

Step 2:建立 XSL 樣板檔

接著建立 XSL 樣板檔,其主體結構如下:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <html>
      <head>
        <title>
          …
        </title>
        <style>
          …
        </style>
      </head>
    </html>
    <body>
        …
    </body>
  </xsl:template>
</xsl:stylesheet>

其實 XSL 檔案與 .html 十分相似,裡面有 <html />、<head/> 或 <body/> 等區段的宣告,而使用方式也幾乎是一樣,剩下的工作就是把細部定義填滿,然後供 XML 檔套用即可,以下為完整的 Transform.xsl 定義:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="//summary/heading"/>
        </title>
        <style>
          th{
          background-color:lightblue;
          }

          h1{
          color:red;
          }
        </style>
      </head>

      <body>
        <h1>
          <xsl:value-of select="//summary/heading"/>
        </h1>
        <h2>
          <xsl:value-of select="//summary/subhead"/>
        </h2>
        <p>
          <xsl:value-of select="//summary/description"/>
        </p>

        <table border="1">
          <tr>
            <th>縣市\季</th>
            <xsl:for-each select="//data/region[1]/quarter">
              <th>
                Q<xsl:value-of select="@number"/>
              </th>
            </xsl:for-each>
            <th>Total</th>
          </tr>
          <xsl:for-each select="//data/region">
            <tr>
              <th style="text-align:left">
                <xsl:value-of select="name"/>
              </th>
              <xsl:for-each select="quarter">
                <td style="text-align:right">
                  <xsl:value-of select="format-number(@books_sold,'###,###')"/>
                </td>
              </xsl:for-each>
              <td style="text-align:right;font-weight:bold;" >
                <xsl:value-of select="format-number(sum(quarter/@books_sold),'###,###')"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

程式說明:

  1. XSL 從 XML 讀取資料是透過 <xsl:value-of select="//summary/heading"/> 這樣型式的語法,select 屬性指定欲讀取的節點名稱,就能自動載入節點資料。
  2. 而 <xsl:for-each select="//data/region[1]/quarter">…</xsl:for-each> 這種語法則是用迴圈的原理,反覆從 XML 節點讀取資料。

Step 3:XML 指定套用 XSL 樣板

XML 指定套用 XSL 樣板有兩種方式,一是靜態套用,二是動態套用,靜態套用是指在 XML 中明確指定 XSL 檔,而動態套用是在 Runtime 執行時,再以程式動態指定 XSL 進行套用。例如原本 Sales.xml 檔中第二行為刪除狀態,意思是不要加入,而在這個步驟中將加入以下宣告,完成 XSL 的靜態套用:

<?xml-stylesheet type="text/xsl" href="transform.xsl" ?>

Step 4:執行 XML

最後請以瀏覽器瀏覽 Sales.xml 檔,執行畫面如下圖。


圖1 XML 套用 XSL 樣板後之定義

解析 Email.cs 與 EmailTemplate.cs 程式

在瞭解較為困難的 XSL 原理之後,現在回過頭來看 Email.cs 與 EmailTemplate.cs 兩個程式,Email.cs 本身是負責純粹的電子郵件寄送的作業,而 EmailTemplate.cs 則是掌管 XSL 樣板檔的套用。

  • EmailTemplate.cs 程式解析

    讓我們先從 EmailTemplate.cs 程式看起,其中最重要的就是產生電子郵件 body 內容的 GenerateEmailBody 方法,而此方法最核心的就是「動態套用 XSL」的語法,以下是重點語法:

public static string GenerateEmailBody(string templatePath, XNode xmlInputData)
{
    string strHtml = string.Empty;
    MemoryStream memoryStream = new MemoryStream();
    StreamWriter writer = new StreamWriter(memoryStream,                Encoding.UTF8);
    XmlTextWriter htmlWriter = new XmlTextWriter(writer);
    StreamReader streamReader = new StreamReader(memoryStream);

    try
    {
        DebugLog("Starting Transformation for template: {0}...",            templatePath);
        DebugLog("Transformation Input Data:\n{0}",                         xmlInputData.ToString());

        // 載入 XSL 定義
        XslCompiledTransform xslCompiledTransform = new                     XslCompiledTransform();
        xslCompiledTransform.Load(templatePath);

        xslCompiledTransform.Transform(
            xmlInputData.CreateNavigator(),
            null,
            htmlWriter);

        memoryStream.Position = 0;
        strHtml = streamReader.ReadToEnd();
    }
    catch (XsltException xsltException)
    {
        …
    }
    catch (Exception ex)
    {
        …
    }
    finally
    {
        …
    }

    DebugLog("Transformation Result:\n{0}", strHtml);

    return strHtml;
}

程式說明:

  1. 載入與套用 XSL 的程式則是為 XslCompiledTransform 開始的 3 行程式,最後再以 strHtml 回傳套用 XSL 後的完整 HTML 內容,指定到電子郵件之中。
  2. DebugLog 方法的作用是寫入 Log 資訊到C:\TemplateDrivenEmail\code\CSharp\TemplateDrivenEmail.SimpleEmail\App_Data\Log\Transformations.log 的記錄檔之中。
  • Email.cs 程式解析

    Email.cs 程式裡面只有三類方法,Send、CreateMailMessage 和 CreateInlineAttachment 方法,Send 是以多載方法型式撰寫,功用是發送電子郵件,而 CreateMailMessage 也是多載方法型式,用於建立電子郵件內容,至於 CreateInlineAttachment 方法則是處理電子郵件的 Attachment 附件。

    例如 TemplateDrivenEmail.SimpleEmail 專案的 Default.aspx 程式,其中以 SendTemplateDrivenEmailAdvanced 方法發送套用複雜 XSL 樣板的電子郵件,其中發送郵件的程式只需要呼叫 Email.Send 方法即可,省去了重複撰寫發送 Mail 程式的不必要性,以下為程式碼:

  • C# 語法

protected void SendTemplateDrivenEmailAdvanced()
{
    string advancedTemplatePath =   Path.Combine(HttpRuntime.AppDomainAppPath,  "App_Data/EmailTemplates/AdvancedTemplate.xsl");

    XDocument xmlInputData = GenerateXmlData();

    Email.Send(this.recipientTextBox.Text, this.subjectTextBox.Text, advancedTemplatePath, xmlInputData);
}
  • VB 語法
Protected Sub SendTemplateDrivenEmailAdvanced()
    Dim advancedTemplatePath =  Path.Combine(HttpRuntime.AppDomainAppPath,  "App_Data/EmailTemplates/AdvancedTemplate.xsl")

    Dim xmlInputData = GenerateXmlData()

    Email.Send(Me.recipientTextBox.Text, Me.subjectTextBox.Text,    advancedTemplatePath, xmlInputData)
End Sub

三.VS 2008 對 XSL 測試與偵錯支援性

對 XSL 的程式開發,VS 2008 還供了 XSL 顯示與偵錯兩個功能,在開發過程中可提供方便的輔助,說明如下。

  1. 顯示 XSL Transformation 輸出結果

    若欲透過 VS 2008 顯示 XSL Transformation 輸出結果,請開啓一個XSL檔,選擇 VS 2008的【XML】選單【顯示 XSLT 輸出】,系統會要求指定 XML 檔資料來源,然後在 VS 2008 就可直接顯示 XSL 套用後的畫面。


    圖2 顯示 XSLT 輸出


    圖3 XML 套用 XSLT 輸出結果

     

  2. XSL Transformation 偵錯

    VS 2008 對 XSL 還支援偵錯功能,例如在 XSL 中設定中斷點(Breakpoint),選擇【XML】選單【偵錯XSLT】,便可像 C# 及 VB 程式進行逐步偵錯。


    圖4 XSL 偵錯

四.結論

在介紹 XSL 的作用及原理後,您應能體會套用 XSL 樣板的優點,同時也更能理解將 Mail 發送程式獨立成元件的重複使用好處,您不妨試著將 Mail 元件及 XSL 樣板程式技巧引入您的專案,為程式帶來更好的美感與最佳化。