目的:
实现用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登录,而是跳转回登录页面。
至此我的代码迭代结束了。
代码下载:
http://www.cnblogs.com/files/ayuan/masterpagebasedemo.rar
本人之前没有写技术文章的经验,所以以上的文字难免晦涩,而且自身技术水平也有限,可能有些观点不太成熟,欢迎各位朋友指正。
新闻热点
疑难解答
图片精选