目的: 實現用MasterPage中的.cs檔案 代替項目中的PageBase。 動機: 寫這篇文章的動機,來自於一次項目重構。在.Net Framwork 2.0的B/S架構項目中同時採用PageBase和MasterPage技術,發現每次訪問頁面,頁面同時訪問PageBase和MasterPage,不僅造成效能降低,甚至有可能給日後的項目功能擴充和調整帶來邏輯錯誤隱患。 技術環節: PageBase:.Net Framework 1.1 中經常使用的一種封裝多個頁面相同功能的技術。PageBase.cs類繼承自System.Web.UI.Page類,項目中的Web頁面繼承自PageBase.cs類,通過重寫基類中的頁面初始化方法,實現調用PageBase中的業務功能,例如:url參數驗證,儲存訪問量等功能(具體實現方式參見微軟官方例子duwamishi)。 MasterPage:.Net Framework 2.0 中新特性,物理上包括兩個檔案,分別是:.Master檔案(html標記),.cs檔案(C#代碼)。.Master檔案實現顯示層繪製,.cs檔案實現具體功能。繼承自MasterPage的Web頁面可以繼承MasterPage中的顯示層內容。繪製通用的頁頭頁尾,定製統一的布局,MasterPage是不錯的選擇。 類比需求: 用MasterPage技術,代替PageBase,實現地址欄參數驗證。 簡單的做個解釋吧,資料庫中Login表資訊如: 登入系統之後,url地址欄中帶有參數,如下: http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1001 此時使用者手動修改url地址欄中參數為: http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1002 被視為非法操作,系統將自動跳回登入頁面。 第一次代碼迭代: 1.參照傳統PageBase方法: 傳統的Page做法為: public class PageBase : System.Web.UI.Page { public PageBase() { } /**//// <summary> /// 入口方法 /// </summary> protected void Initialize() { // 插入通用商務邏輯 } } Web頁面: public partial class TestPage : PageBase { // 傳統的調用PageBase的方法 /**///// <summary> /// 重寫基類OnPreInit() 方法,調用通用驗證方法 /// </summary> /// <param name="e"></param> protected override void OnInit(eventargs e) { base.Initialize(); } } 參照其做法,將PageBase中的代碼移入MasterPage中: MasterPage.cs: public partial class MyMasterPage : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { // 調用驗證方法 Initialize(); } } } 將Web頁面中的代碼修改為: public partial class TestPage : System.Web.UI.Page { // 仿照PageBase方法,調用Master中的方法 /**//// <summary> /// 重寫基類OnPreInit() 方法,調用通用驗證方法 /// </summary> /// <param name="e"></param> protected override void OnInit(eventargs e) { // 獲得母板頁引用 MyMasterPage myMasterPage = (MyMasterPage)this.Master; // 調用母板頁中通用驗證方法 if (!IsPostBack) { myMasterPage.Initialize(); } } }將MasterPage中的Initialize()方法替換為執行個體中的,測試代碼: 步驟1:用 使用者名稱zhangsan登入系統,登入成功, 頁面顯示 歡迎 zhangsan 登入。 url地址顯示: http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1001 步驟2:手動修改url地址欄:如下: http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1002 頁面不會顯示 歡迎lisi登入,而是跳回登入頁面。 反思:雖然功能實現,但是存在不理想的環節: 1. Master中的被子類調用方法必須是public方法; 2. 雖然不用修改Web頁的繼承,但是依然要機械的複製粘貼重寫基類的OnInit()方法。 為了消除這些懷味道,於是開始: 第二次代碼迭代: 修改MasterPage.cs中的代碼: public partial class MyMasterPage : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { // 調用驗證方法 CheckLogin(); } } /**//// <summary> /// 驗證訪問是否合法 /// </summary> private void CheckLogin() { // 如果 url中的編號 或 cookie中的編號 if (string.IsNullOrEmpty(Request.QueryString["id"]) || string.IsNullOrEmpty(CookieUtil.ReadCookieByKey("id"))) { Response.Redirect("Login.aspx"); }// 如果url中的編號 和 cookie中的編號 不匹配,返回登入頁 else if (int.Parse(Request.QueryString["id"]) != int.Parse(CookieUtil.ReadCookieByKey("id"))) { Response.Redirect("Login.aspx"); } } }重構之後,Web頁可以不進行任何修改,MasterPage在自身的Page_Load()方法中自動調用驗證方法,而且將驗證方法設定為private,僅供MasterPage自身調用,提高安全性。至此,代碼似乎比較理想了,測試: 步驟一:用 使用者名稱 zhangsan登入系統, 依然顯示使用者登入頁面。 測試失敗。 用斷點跟蹤代碼,發現問題出現在MasterPage.cs中的CheckLogin()方法中的程式碼片段: if (string.IsNullOrEmpty(Request.QueryString["id"]) || string.IsNullOrEmpty(CookieUtil.ReadCookieByKey("id"))) { Response.Redirect("Login.aspx"); } 由於登入頁繼承自MasterPage,所以頁面載入時自動調用MasterPage.cs中的驗證方法,而自身的參數又不滿足string.IsNullOrEmpty()方法,於是又跳回到登入頁面,登入頁面在再次在載入時調用基類中的驗證方法,於是形成死迴圈。 在PageBase技術中,Web頁面可以有選擇的繼承自PageBase,而MasterPage技術中,為了獲得一致的顯示層效果,Web頁面對繼承MasterPage的選擇性是非常底的,而且我們也不應該採用建立相同顯示,不帶有驗證代碼的MasterPage,來給不需要繼承基類功能的Web頁面來繼承,這種方式顯然不合理。為瞭解決這個問題,於是開始了 第三次迭代: 引入設定檔: <?xml version="1.0" encoding="utf-8" ?> <pages> <testpage> <page title="TestPage" url="TestPage.aspx" needvalidate="true"/> <page title="Login" url="Login.aspx" needvalidate="false"/> </testpage> <adminpages> <page title="Page1" url="~/Admin/Page1.aspx" needvalidate="false"/> <page title="Page2" url="~/Admin/Page2.aspx" needvalidate="false"/> </adminpages> </pages> 從中可以看到,將需要驗證的頁面加以標識(needvalidate="true")。 建立Xml資料訪問類: public class XmlDAL { private static string filePath = string.Empty; static XmlDAL() { // 初始化設定檔路徑 filePath = HttpContext.Current.Request.MapPath("~/App_Data/xml/" + "Pages.xml"); } /**//// <summary> /// 獲得需要驗證的頁面列表 /// </summary> /// <returns>需要驗證的頁面列表</returns> public static IList<string> GetValidatePages() { IList<string> pages = new List<string>(); // 如果指定設定檔存在 if (System.IO.File.Exists(filePath)) { try { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(filePath); // 擷取設定檔根節點 XmlNode root = xmlDoc.DocumentElement; string xpath = "/pages/testpage/page[@needvalidate='true']"; XmlNodeList nodeList = root.SelectNodes(xpath); // 便利節點集合 foreach (XmlNode node in nodeList) { pages.Add(node.Attributes["title"].Value); } } catch (Exception ex) { throw new Exception(ex.Message); } } return pages; } } 重構MasterPage.cs中的代碼,加入IsValidateNeeded(string url)方法,用於檢測當前頁面是否需要驗證,修改驗證方法: public partial class MyMasterPage : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { // 調用驗證方法 CheckLogin(); } } /**//// <summary> /// 驗證訪問是否合法 /// </summary> private void CheckLogin() { // 判斷當前訪問頁面是否需要進行驗證 if (IsValidateNeeded(Request.RawUrl)) { // 如果 url中的編號 或 cookie中的編號 if (string.IsNullOrEmpty(Request.QueryString["id"]) || string.IsNullOrEmpty(CookieUtil.ReadCookieByKey("id"))) { Response.Redirect("Login.aspx"); }// 如果url中的編號 和 cookie中的編號 不匹配,返回登入頁 else if (int.Parse(Request.QueryString["id"]) != int.Parse(CookieUtil.ReadCookieByKey("id"))) { Response.Redirect("Login.aspx"); } } } /**//// <summary> /// 驗證當前頁是否需要驗證 /// </summary> /// <param name="currentPage">當前頁面名稱</param> /// <returns>是否需要驗證狀態</returns> private bool IsValidateNeeded(string url) { bool isNeeded = false; // GetValidatePages() 方法返回需要驗證頁面列表 IList<string> pages = XmlDAL.GetValidatePages(); IEnumerator<string> ie = pages.GetEnumerator(); while (ie.MoveNext()) { // 如果當前頁面需要進行驗證 if (url.Contains(ie.Current)) // 返回需要驗證狀態 return isNeeded = true; } return isNeeded; } } 進行測試: 步驟1:用 使用者名稱zhangsan登入系統,登入成功, 頁面顯示 歡迎 zhangsan 登入。 url地址顯示: http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1001 步驟2:手動修改url地址欄:如下: http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1002 頁面不會顯示 歡迎lisi登入,而是跳回登入頁面。 至此My Code迭代結束了。 代碼下載: http://www.cnblogs.com/Files/ayuan/MasterPageBaseDemo.rar 本人之前沒有寫技術文章的經驗,所以以上的文字難免晦澀,而且自身技術水平也有限,可能有些觀點不太成熟,歡迎各位朋友指正。 |