ASP.NET 状态管理(查询字符串 和 跨页回发)
2012-07-12 17:48
423 查看
视图状态一个最大的限制是它必须和特定页面紧密绑定。当用户从一个页面浏览到另一个页面时,这些信息也就消失了。这个问题有几种解决方案,最佳的方案取决于你的项目需求。
http://www.google.ca/search?q=organic+gardening
查询字符串
一个常用的办法是在URL中使用查询字符串传送信息。搜索引擎中频繁的使用了这种办法。查询字符串的优势:
它是轻量级的,并且不会加重服务器的负担。和跨页回发不同,查询字符串很容易在页面间传送相同的信息。
查询字符串的限制:
信息仅限于简单的字符串,只能包含合法的URL字符。
用户很容易看到信息,对因特网上的窃听者也是如此。
大胆的用户可能会修改查询字符串或给它赋新值,而你的程序并不能预期和预防这些修改。
多数浏览器对URL字符串的长度有限制(通常1K-2K)
尽管如此,把信息加入到查询字符串中仍是一项很有用的技术。
使用查询字符串
没有基于集合的方式来帮助你放置信息,你需要自己放置查询字符串的存储信息。intrecordID=10;
Response.Redirect("newpage.aspx?recordID="+recordID.ToString());
可以发送多个参数,中间用符号&隔开。
接收页面很容易就可以使用查询字符串来工作,它使用内置的Request对象提供的QueryString字典集合来取值:
stringID=Requet.QueryString["recordID"];
如果集合中不包含查询的Key值,那么ID将被设为null值。
取得的值总是字符串,也因此很容易就可以转换为其它数据类型。
URL编码
查询字符串的一个潜在问题是使用URL中不允许的字符(URL中所有字符必须是字母、数字、及少量的符号"$-.+!*'(),")。如果担心将要在查询字符串中保存的数据含有非法字符,可以使用HttpServerUtility类中提供的方法进行编码。stringproductName="FlyingCarpet";
Response.Redirect("newpage.aspx?productName="+Server.UrlEncode(productName));
你可以使用Server.UrlDecode()方法将字符串恢复其初始值,但不需要这么做,当使用Request.QueryString集合时,ASP.NET自动解码了该值。
跨页回发
只不过是把信息从一个页面发送到另一个页面去,这个技术听起来很简单,但却是一个潜在的雷区!使用的不好,会导致创建的页面紧密耦合而难于改进和调试。支持跨页回发的基础架构是属性PostBackUrl,它在IButtonControl接口中定义并在按钮类控件(Button、LinkButton、ImageButton)中出现。只需要简单地将PostBackUrl设置为其他Web窗体的名字就可以使用跨页回发了。
看下面的小示例:
<%@PageLanguage="C#"AutoEventWireup="true"CodeFile="CrossPage1.aspx.cs"Inherits="Chapter06_CrossPage1"%>
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<htmlxmlns="http://www.w3.org/1999/xhtml">
<headrunat="server">
<title>CrossPage1</title>
</head>
<body>
<formid="form1"runat="server">
<div>
<asp:TextBoxID="txtFirstName"runat="server"></asp:TextBox>
<asp:TextBoxID="txtLastName"runat="server"></asp:TextBox>
<asp:ButtonID="btnSubmit"runat="server"PostBackUrl="CrossPage2.aspx"
Text="Submit"/>
</div>
</form>
</body>
</html>
CrossPage2.aspx可以通过PreviousPage属性和CrossPage1.aspx中的对象进行交互:
protectedvoidPage_Load(objectsender,EventArgse)
{
if(Page.PreviousPage!=null)
{
lblInfo.Text="Youcamefromapagetitled"+PreviousPage.Header.Title;
}
}
1.获取页面特定信息
在先前的示例中,仅仅是获得了Page类的成员。如果要得到更具体的细节,如控件的值,必须把PreviousPage转换为适当的类型:protectedvoidPage_Load(objectsender,EventArgse)
{
Chapter06_CrossPage1prePage=PreviousPageasChapter06_CrossPage1;
if(prePage!=null)
{
//(Readsomeinfomationfrompreviouspage.)
}
}
注:
对于没有项目文件的网站,VS会标记为错误,表示它没有源页面类的类型信息(如CrossPage1),不过编译后这个错误会消失。
即便已经将前一页面转换成了正确的页面类型,但还是不能够直接访问控件的值。这是因为控件被声明为了受保护的成员,此时,可以通过在页面类中添加封装控件变量的属性来实现上述操作:
publicTextBoxFirstNameTextBox
{
get{returntxtFirstName;}
}
publicTextBoxLastNameTextBox
{
get{returntxtLastName;}
}
但这不是好的方式,它显示了太多的细节,还允许目标页面随意访问控件的任意属性!最好的方式是定义特定且有效的方法或属性来提取需要的信息:
publicstringFullName
{
get{returntxtFirstName.Text+txtLastName.Text;}
}
现在已经达到最好的状态了。两个页面的关系被文档化且易于理解。即使源页面控件改变了,仍只需要修改FullName属性,修改仅限于CrossPage1.aspx页面,而完全不需要修改CrossPage2.aspx页面。
2.在任意事件处理程序中进行跨页发送
除了使用实现了IButtonControl接口的那些按钮进行跨页发送外,我们还可以使用一个重载的Server.Transfer()方法将试图状态信息完整的送到目标页面。只要简单的加上一个值为true的oreserverFrom参数即可。//为true,则保留System.Web.HttpRequest.QueryString和System.Web.HttpRequest.Form集合。
//为false,则清除System.Web.HttpRequest.QueryString和System.Web.HttpRequest.Form集合。
Server.Transfer("CrossPage2.aspx",true);
这样就可以在网页代码的任意地方使用跨页发送了(只要能访问到Server对象的地方)。
该技术会造成服务器的重定向,这意味着没有额外的往返来重定向客户,即使已去了另一个页面,但客户端浏览器中的URL不会改变。
怎么区分是通过一个按钮跨页传送还是通过Server.Transfer()方法跨页传送的呢?
protectedvoidPage_Load(objectsender,EventArgse)
{
if(PreviousPage==null)
{
//直接被get或post方式请求
}
elseif(PreviousPage.IsCrossPagePostBack)
{
//通过Button类跨页回传
}
else
{
//通过Server.Transfer()跨页回传
}
}
3.IsPostBack属性和IsCrossPagePostBack属性
理解Page.IsPostBack属性在跨页回送中是很重要的。源页面(触发了跨页回送的页面)的IsPostBack属性为true。目的页面(接收跨页回送的页面)的IsPostBack属性为false。这个系统的好处是,它意味着你的初始化代码通常会在该运行的时候运行。假设第一次请求CrossPage1.aspx使用如下代码时,它执行了一些费时的初始化工作:
protectedvoidPage_Load(objectsender,EventArgse)
{
if(!Page.IsPostBack)
{
//Retrievesomedatafromadatabaseanddisplayitonthepage.
}
}
现在假设用户通过跨页回送从页面1转到了页面2。只要CrossPage2.aspx方位PreviousPage属性,就会执行CrossPage1.aspx页面生命周期。此时,再次触发CrossPage1.aspx的Page.Load事件。然而,CrossPage1.aspx上的Page.IsPostBack属性为true,你的代码就会跳过费时的初始化步骤,控件的值从视图状态得以恢复。另一方面,CrossPage2.aspx的IsPostBack属性为false,所有该页面执行必要的第一次初始化。
在某些情况下,当页面不是跨页回送的源页面时,你可能会有用于处理第一次请求和所有接下来的回送的代码。在这种情况下,你可以查看IsCrossPagePostBack属性,如果当前页面触发了一个跨页回送,则该属性的值为true。
protectedvoidPage_Load(objectsender,EventArgse)
{
if(Page.IsCrossPagePostBack)
{
//ThispagetriggeredapostbacktoCrossPage2.aspx.
}
elseif(Page.IsPostBack)
{
//Thispagewaspostedbacknormally.
//Don'tdothefirst-requestinitialization.
}
else
{
//Thisisthefirstrequestforthepage.
//Performalltherequiredinitialization.
}
}
4.跨页发送和验证
在跨页回送中使用验证有一些潜在的麻烦。如果源页面包含验证控件,那么使用跨页发送会发生什么呢?两个按钮都将CausesValidation设置为true,因此单击任何一个按钮来触发跨页回发,回发都会被浏览器的客户端验证阻止,并出现错误信息。此时将RequiredFieldValidator验证控件的属性EnableClientScript设为false以模拟客户端不支持JavaScript脚本或恶意客户避开了客户端验证,再次单击任一按钮,页面回发,此时新页面出现了。
为了避免发生这种问题,显然,在执行任意动作前必须检查Page.IsValid以在目标页面中保证源页面有效。这是需要验证的Web窗体的标准防范。不同的是,当页面无效时什么都不做事不够的,我们往往需要采用一些步骤将用户带回到原始页面:
//Thiscodeisintargetpage.
protectedvoidPage_Load(objectsender,EventArgse)
{
if(PreviousPage!=null)
{
if(!PreviousPage.IsValid)
{
//Displayanerrormessageorjustdonothing.
//Response.Redirect("CrossPage1.aspx");
}
else
{
//...
}
}
}
上面这段代码还可以再度改进。现在,因为页面是被重新请求的(不是回发),所有当用户回到原始页面时,错误信息不回出现。要修正这个问题,可以设置一个标记让原始页面知道,请求已被目标页面拒绝。
protectedvoidPage_Load(objectsender,EventArgse)
{
if(PreviousPage!=null)
{
if(!PreviousPage.IsValid)
{
//UrlReferrer:获取有关客户端上次请求的URL的信息(包含许多信息)
//AbsolutePath:获取URI的绝对路径
Response.Redirect(Request.UrlReferrer.AbsolutePath+"?err=true");
}
else
{
//...
}
}
}
现在,原始页面只需要检查查询字符串中的标记并再次执行验证,验证将显示无效数据的信息。
protectedvoidPage_Load(objectsender,EventArgse)
{
if(Request.QueryString["err"]!=null)
{
Page.Validate();
}
}
还可以做更多来改进页面。比如说,用户已经填写了某一详细表单的一部分,这时候再重新请求页面就不太好,因为这样就清空了用户的输入,用户将不得不重新开始。你应该在响应流中写一些JavaScript代码,它使用浏览器的回退功能回到源页面。
这个示例演示了跨页回发通常要比开发人员想象的麻烦。如果没有小心处理,跨页回发会导致生成紧耦合的页面,它们相互有依赖性,这使得它们将来的修改变得困难。