用MasterPage 代替 PageBase

目的:

實現用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表信息如下圖:

用MasterPage 代替 PageBase

登錄系統之後,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登錄,而是跳轉回登錄頁面。

至此我的代碼叠代結束了。

代碼下載:

http://www.cnblogs.com/Files/ayuan/MasterPageBaseDemo.rar

本人之前沒有寫技術文章的經驗,所以以上的文字難免晦澀,而且自身技術水平也有限,可能有些觀點不太成熟,歡迎各位朋友指正。

 
用MasterPage 代替 PageBase
用MasterPage 代替 PageBase
目的: 實現用MasterPage中的.cs文件 代替項目中的PageBase。動機: 寫這篇文章的動機,來自于一次項目重構。在.Net Framwork 2.0的B/S架構項目中同時采用PageBase和MasterPage技術,發現每次...查看完整版>>用MasterPage 代替 PageBase
 
Localization/MasterPage in ASP.NET 1.1 with Spring.Net
1. Get spring.core/spring.web from sourceforge's CVS2.Include Spring.Core,Spring.Web in your new Web App3.Change your Web.Config like:<configuration> <configSections> <section...查看完整版>>Localization/MasterPage in ASP.NET 1.1 with Spring.Net
 
ASP.NET 2.0配合MasterPage的優化CSS
  ASP.NET 2.0 中增加了內建的 MasterPage 的支持,這對我們來說是一個很大的便利。然而經過一段時間的使用,我發現 MasterPage 並不是那麽完美:嵌套的 MasterPage 不能支持設計時界面,以及下面要提到的Content ...查看完整版>>ASP.NET 2.0配合MasterPage的優化CSS
 
Localization/MasterPage in ASP.NET 1.1 with Spring.Net
1. Get spring.core/spring.web from sourceforge's CVS2.Include Spring.Core,Spring.Web in your new Web App3.Change your Web.Config like:<configuration> <configSections> <section...查看完整版>>Localization/MasterPage in ASP.NET 1.1 with Spring.Net
 
在MasterPage中檢驗User是否登入
在母板頁中檢查user是否登入過,這樣就不用在每個頁中去作檢驗。在其Init事件中寫入如下代碼: protected void ContentPlaceHolder1_Init(object sender, EventArgs e) { if (Session["loginid"] == nu...查看完整版>>在MasterPage中檢驗User是否登入