[开发技巧] 用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表信息如下图:             
                                
登录系统之后,url地址栏中带有参数,如下:
http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1001
此时用户手动修改url地址栏中参数为:
http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1002
被视为非法操作,系统将自动跳转回登录页面。


第一次代码迭代:

1.参照传统PageBase方法:
        传统的Page做法为:
None.gif public   class  PageBase : System.Web.UI.Page
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {    
InBlock.gif    
public PageBase()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 入口方法
ExpandedSubBlockEnd.gif    
/// </summary>

InBlock.gif    protected void Initialize()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
// 插入通用业务逻辑
ExpandedSubBlockEnd.gif
     }

ExpandedBlockEnd.gif}

        Web页面:
None.gif public  partial  class  TestPage : PageBase
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
// 传统的调用PageBase的方法     
ExpandedSubBlockStart.gifContractedSubBlock.gif
    /**////<summary>
InBlock.gif    
/// 重写基类OnPreInit() 方法,调用通用验证方法
InBlock.gif    
/// </summary>
ExpandedSubBlockEnd.gif    
/// <param name="e"></param>

InBlock.gif    protected override void OnInit(eventargs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
base.Initialize();
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

参照其做法,将PageBase中的代码移入MasterPage中:
MasterPage.cs:
None.gif public  partial  class  MyMasterPage : System.Web.UI.MasterPage
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
protected void Page_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
if (!IsPostBack)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
// 调用验证方法
InBlock.gif
            Initialize();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

将Web页面中的代码修改为:
None.gif public  partial  class  TestPage : System.Web.UI.Page
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {    
InBlock.gif    
// 仿照PageBase方法,调用Master中的方法  
ExpandedSubBlockStart.gifContractedSubBlock.gif
    /**//// <summary>
InBlock.gif    
/// 重写基类OnPreInit() 方法,调用通用验证方法
InBlock.gif    
/// </summary>
ExpandedSubBlockEnd.gif    
/// <param name="e"></param>

InBlock.gif    protected override void OnInit(eventargs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{        
InBlock.gif        
// 获得母板页引用
InBlock.gif
        MyMasterPage myMasterPage = (MyMasterPage)this.Master;
InBlock.gif        
// 调用母板页中通用验证方法
InBlock.gif
        if (!IsPostBack)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            myMasterPage.Initialize();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}
将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中的代码:
None.gif public  partial  class  MyMasterPage : System.Web.UI.MasterPage
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
protected void Page_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
if (!IsPostBack)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
// 调用验证方法
InBlock.gif
            CheckLogin();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 验证访问是否合法
ExpandedSubBlockEnd.gif    
/// </summary>

InBlock.gif    private void CheckLogin()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{      
InBlock.gif        
// 如果 url中的编号 或 cookie中的编号
InBlock.gif
        if (string.IsNullOrEmpty(Request.QueryString["id"])
InBlock.gif            
|| string.IsNullOrEmpty(CookieUtil.ReadCookieByKey("id")))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            Response.Redirect(
"Login.aspx");
ExpandedSubBlockEnd.gif        }
// 如果url中的编号 和 cookie中的编号 不匹配,返回登录页        
InBlock.gif
        else if (int.Parse(Request.QueryString["id"]) != int.Parse(CookieUtil.ReadCookieByKey("id")))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            Response.Redirect(
"Login.aspx");
ExpandedSubBlockEnd.gif        }
      
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}
重构之后,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页面来继承,这种方式显然不合理。为了解决这个问题,于是开始了
第三次迭代:
引入配置文件:
None.gif <? xml version="1.0" encoding="utf-8"  ?>
None.gif
< pages >
None.gif  
< testpage >
None.gif    
< page  title ="TestPage"  url ="TestPage.aspx"  needvalidate ="true" />
None.gif    
< page  title ="Login"  url ="Login.aspx"  needvalidate ="false" />
None.gif  
</ testpage >
None.gif  
< adminpages >
None.gif    
< page  title ="Page1"  url ="~/Admin/Page1.aspx"  needvalidate ="false" />
None.gif    
< page  title ="Page2"  url ="~/Admin/Page2.aspx"  needvalidate ="false" />
None.gif  
</ adminpages >
None.gif
</ pages >

从中可以看到,将需要验证的页面加以标识(needvalidate="true")。
创建Xml数据访问类:
None.gif public   class  XmlDAL
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
private static string filePath = string.Empty;
InBlock.gif    
static XmlDAL()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
// 初始化配置文件路径
InBlock.gif
        filePath = HttpContext.Current.Request.MapPath("~/App_Data/xml/" + "Pages.xml");
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 获得需要验证的页面列表
InBlock.gif    
/// </summary>
ExpandedSubBlockEnd.gif    
/// <returns>需要验证的页面列表</returns>

InBlock.gif    public static IList<string> GetValidatePages()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        IList
<string> pages = new List<string>();
InBlock.gif        
// 如果指定配置文件存在
InBlock.gif
        if (System.IO.File.Exists(filePath))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{            
InBlock.gif            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{                
InBlock.gif                XmlDocument xmlDoc 
= new XmlDocument();                 
InBlock.gif                xmlDoc.Load(filePath);
InBlock.gif                
// 获取配置文件根节点
InBlock.gif
                XmlNode root = xmlDoc.DocumentElement;
InBlock.gif                
string xpath = "/pages/testpage/page[@needvalidate='true']";
InBlock.gif                XmlNodeList nodeList 
= root.SelectNodes(xpath);
InBlock.gif                
// 便利节点集合
InBlock.gif
                foreach (XmlNode node in nodeList)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    pages.Add(node.Attributes[
"title"].Value);
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

InBlock.gif            
catch (Exception ex)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
throw new Exception(ex.Message);
ExpandedSubBlockEnd.gif            }
            
ExpandedSubBlockEnd.gif        }

InBlock.gif        
return pages;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

重构MasterPage.cs中的代码,加入IsValidateNeeded(string url)方法,用于检测当前页面是否需要验证,修改验证方法:
None.gif public  partial  class  MyMasterPage : System.Web.UI.MasterPage
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
protected void Page_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
if (!IsPostBack)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
// 调用验证方法
InBlock.gif
            CheckLogin();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 验证访问是否合法
ExpandedSubBlockEnd.gif    
/// </summary>
InBlock.gif    private void CheckLogin()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
// 判断当前访问页面是否需要进行验证
InBlock.gif
        if (IsValidateNeeded(Request.RawUrl))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
// 如果 url中的编号 或 cookie中的编号
InBlock.gif
            if (string.IsNullOrEmpty(Request.QueryString["id"])
InBlock.gif                
|| string.IsNullOrEmpty(CookieUtil.ReadCookieByKey("id")))
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                Response.Redirect(
"Login.aspx");
ExpandedSubBlockEnd.gif            }
// 如果url中的编号 和 cookie中的编号 不匹配,返回登录页        
InBlock.gif
            else if (int.Parse(Request.QueryString["id"]) != int.Parse(CookieUtil.ReadCookieByKey("id")))
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                Response.Redirect(
"Login.aspx");
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 验证当前页是否需要验证
InBlock.gif    
/// </summary>
InBlock.gif    
/// <param name="currentPage">当前页面名称</param>
ExpandedSubBlockEnd.gif    
/// <returns>是否需要验证状态</returns>

InBlock.gif    private bool IsValidateNeeded(string url)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
bool isNeeded = false;
InBlock.gif        
// GetValidatePages() 方法返回需要验证页面列表
InBlock.gif
        IList<string> pages = XmlDAL.GetValidatePages();
InBlock.gif        IEnumerator
<string> ie = pages.GetEnumerator();
InBlock.gif        
while (ie.MoveNext())
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
// 如果当前页面需要进行验证
InBlock.gif
            if (url.Contains(ie.Current))
InBlock.gif                
// 返回需要验证状态
InBlock.gif
                return isNeeded = true;
ExpandedSubBlockEnd.gif        }

InBlock.gif        
return isNeeded;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

进行测试:
        步骤1:用 用户名zhangsan登录系统,登录成功,
                      页面显示 欢迎 zhangsan 登录。
                      url地址显示:
                       http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1001
        步骤2:手动修改url地址栏:如下:
                      http://localhost:3730/MasterPageBaseDemo/TestPage.aspx?id=1002
        页面不会显示 欢迎lisi登录,而是跳转回登录页面。

至此我的代码迭代结束了。
代码下载:
        /Files/ayuan/MasterPageBaseDemo.rar
本人之前没有写技术文章的经验,所以以上的文字难免晦涩,而且自身技术水平也有限,可能有些观点不太成熟,欢迎各位朋友指正。

转载于:https://www.cnblogs.com/ayuan/archive/2006/11/02/548295.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值