ASP.NET 状态管理(查询字符串 和 跨页回发)

       视图状态一个最大的限制是它必须和特定页面紧密绑定。当用户从一个页面浏览到另一个页面时,这些信息也就消失了。这个问题有几种解决方案,最佳的方案取决于你的项目需求。

 

查询字符串

       一个常用的办法是在 URL 中使用查询字符串传送信息。搜索引擎中频繁的使用了这种办法。

http://www.google.ca/search?q=organic+gardening

       查询字符串的优势:

          它是轻量级的,并且不会加重服务器的负担。和跨页回发不同,查询字符串很容易在页面间传送相同的信息

       查询字符串的限制:

  1. 信息仅限于简单的字符串,只能包含合法的 URL 字符
  2. 用户很容易看到信息,对因特网上的窃听者也是如此。
  3. 大胆的用户可能会修改查询字符串或给它赋新值,而你的程序并不能预期和预防这些修改。
  4. 多数浏览器对 URL 字符串的长度有限制(通常 1K-2K)

       尽管如此,把信息加入到查询字符串中仍是一项很有用的技术

 

使用查询字符串

       没有基于集合的方式来帮助你放置信息,你需要自己放置查询字符串的存储信息。

int recordID = 10;

Response.Redirect("newpage.aspx?recordID=" + recordID.ToString());

可以发送多个参数,中间用符号 & 隔开

       接收页面很容易就可以使用查询字符串来工作,它使用内置的 Request 对象提供的 QueryString 字典集合来取值:

string ID = Requet.QueryString["recordID"];

如果集合中不包含查询的 Key 值,那么 ID 将被设为 null 值

取得的值总是字符串,也因此很容易就可以转换为其它数据类型。

 

URL 编码

       查询字符串的一个潜在问题是使用 URL 中不允许的字符(URL 中所有字符必须是字母、数字、及少量的符号"$ - . + ! * ' ( ) , ")。如果担心将要在查询字符串中保存的数据含有非法字符,可以使用 HttpServerUtility 类中提供的方法进行编码。

string productName = "Flying Carpet";

Response.Redirect("newpage.aspx?productName=" + Server.UrlEncode(productName));

可以使用 Server.UrlDecode() 方法将字符串恢复其初始值,但不需要这么做,当使用 Request.QueryString集合时,ASP.NET 自动解码了该值。

 

 

 

跨页回发

       只不过是把信息从一个页面发送到另一个页面去,这个技术听起来很简单,但却是一个潜在的雷区!使用的不好,会导致创建的页面紧密耦合而难于改进和调试。

 

       支持跨页回发的基础架构是属性 PostBackUrl,它在 IButtonControl 接口中定义并在按钮类控件(Button、LinkButton、ImageButton)中出现。只需要简单地将 PostBackUrl 设置为其他 Web 窗体的名字就可以使用跨页回发了。

 

       看下面的小示例:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="CrossPage1.aspx.cs" Inherits="Chapter06_CrossPage1" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>CrossPage1</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>    
        <asp:TextBox ID="txtFirstName" runat="server"></asp:TextBox>
&nbsp;<asp:TextBox ID="txtLastName" runat="server"></asp:TextBox>
&nbsp;<asp:Button ID="btnSubmit" runat="server" PostBackUrl="CrossPage2.aspx" 
            Text="Submit" /> 
    </div>
    </form>
</body>
</html>

CrossPage2.aspx 可以通过 PreviousPage 属性和 CrossPage1.aspx 中的对象进行交互:

protected void Page_Load(object sender, EventArgs e)
{
    if (Page.PreviousPage != null)
    {
        lblInfo.Text = "You came from a page titled " + PreviousPage.Header.Title;
    }
}

 

1. 获取页面特定信息

       在先前的示例中,仅仅是获得了 Page 类的成员。如果要得到更具体的细节,如控件的值,必须把 PreviousPage 转换为适当的类型:

protected void Page_Load(object sender, EventArgs e)
{
    Chapter06_CrossPage1 prePage = PreviousPage as Chapter06_CrossPage1;
    if (prePage != null)
    {
        // (Read some infomation from previous page.)
    }        
}

注:

       对于没有项目文件的网站,VS 会标记为错误,表示它没有源页面类的类型信息(如 CrossPage1),不过编译后这个错误会消失

 

       即便已经将前一页面转换成了正确的页面类型,但还是不能够直接访问控件的值。这是因为控件被声明为了受保护的成员,此时,可以通过在页面类中添加封装控件变量的属性来实现上述操作:

public TextBox FirstNameTextBox
{
    get { return txtFirstName; }
}
 
public TextBox LastNameTextBox
{
    get { return txtLastName; }
}

       但这不是好的方式,它显示了太多的细节,还允许目标页面随意访问控件的任意属性!最好的方式是定义特定且有效的方法或属性来提取需要的信息:

public string FullName
{
    get { return txtFirstName.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()方法跨页传送的呢?

protected void Page_Load(object sender, EventArgs e)
{
    if (PreviousPage == null)
    {
        // 直接被 get 或 post 方式请求
    }
    else if (PreviousPage.IsCrossPagePostBack)
    {
        // 通过 Button 类跨页回传
    }
    else
    {
        // 通过 Server.Transfer() 跨页回传
    }
}

 

3.   IsPostBack 属性和 IsCrossPagePostBack 属性

       理解 Page.IsPostBack 属性在跨页回送中是很重要的。源页面(触发了跨页回送的页面)的 IsPostBack 属性为 true。目的页面(接收跨页回送的页面)的 IsPostBack 属性为 false。这个系统的好处是,它意味着你的初始化代码通常会在该运行的时候运行

       假设第一次请求 CrossPage1.aspx 使用如下代码时,它执行了一些费时的初始化工作:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        // Retrieve some data from a database and display it on the page.
    }
}

       现在假设用户通过跨页回送从 页面1 转到了 页面2 。只要 CrossPage2.aspx 方位 PreviousPage 属性,就会执行 CrossPage1.aspx 页面生命周期。此时,再次触发 CrossPage1.aspx 的 Page.Load 事件。然而,CrossPage1.aspx 上的 Page.IsPostBack 属性为 true,你的代码就会跳过费时的初始化步骤,控件的值从视图状态得以恢复。另一方面,CrossPage2.aspx 的IsPostBack 属性为 false,所有该页面执行必要的第一次初始化。

 

       在某些情况下,当页面不是跨页回送的源页面时,你可能会有用于处理第一次请求和所有接下来的回送的代码。在这种情况下,你可以查看 IsCrossPagePostBack 属性,如果当前页面触发了一个跨页回送,则该属性的值为 true。

protected void Page_Load(object sender, EventArgs e)
{
    if (Page.IsCrossPagePostBack)
    {
        // This page triggered a postback to CrossPage2.aspx.
    }
    else if (Page.IsPostBack)
    {
        // This page was posted back normally.
        // Don't do the first-request initialization.
    }
    else
    {
        // This is the first request for the page.
        // Perform all the required initialization.
    }
}

 

4. 跨页发送和验证

       在跨页回送中使用验证有一些潜在的麻烦。如果源页面包含验证控件,那么使用跨页发送会发生什么呢?

image

       两个按钮都将 CausesValidation 设置为 true,因此单击任何一个按钮来触发跨页回发,回发都会被浏览器的客户端验证阻止,并出现错误信息。此时将 RequiredFieldValidator 验证控件的属性 EnableClientScript 设为 false 以模拟客户端不支持 JavaScript 脚本或恶意客户避开了客户端验证,再次单击任一按钮,页面回发,此时新页面出现了。

       为了避免发生这种问题,显然,在执行任意动作前必须检查 Page.IsValid 以在目标页面中保证源页面有效。这是需要验证的 Web 窗体的标准防范。不同的是,当页面无效时什么都不做事不够的,我们往往需要采用一些步骤将用户带回到原始页面

// This code is in target page.
protected void Page_Load(object sender, EventArgs e)
{
    if (PreviousPage != null)
    {
        if (!PreviousPage.IsValid)
        {
            // Display an error message or just do nothing.
            // Response.Redirect("CrossPage1.aspx");
        }
        else
        {
            // ...
        }
    }
}

       上面这段代码还可以再度改进。现在,因为页面是被重新请求的(不是回发),所有当用户回到原始页面时,错误信息不回出现。要修正这个问题,可以设置一个标记让原始页面知道,请求已被目标页面拒绝。

protected void Page_Load(object sender, EventArgs e)
{
    if (PreviousPage != null)
    {
        if (!PreviousPage.IsValid)
        {
            // UrlReferrer: 获取有关客户端上次请求的 URL 的信息(包含许多信息)
            // AbsolutePath: 获取 URI 的绝对路径
            Response.Redirect(Request.UrlReferrer.AbsolutePath+"?err=true");
        }
        else
        {
            // ...
        }
    }    
}

       现在,原始页面只需要检查查询字符串中的标记并再次执行验证,验证将显示无效数据的信息。

protected void Page_Load(object sender, EventArgs e)
{
    if (Request.QueryString["err"]!=null)
    {
        Page.Validate();
    }
}

 

       还可以做更多来改进页面。比如说,用户已经填写了某一详细表单的一部分,这时候再重新请求页面就不太好,因为这样就清空了用户的输入,用户将不得不重新开始。你应该在响应流中写一些 JavaScript 代码,它使用浏览器的回退功能回到源页面。

 

       这个示例演示了跨页回发通常要比开发人员想象的麻烦。如果没有小心处理,跨页回发会导致生成紧耦合的页面,它们相互有依赖性,这使得它们将来的修改变得困难。

posted on 2012-07-12 17:48  SkySoot  阅读(2432)  评论(0编辑  收藏  举报

导航