无论 Web 程序框架多么先进,它都不能改变一个事实:HTTP是一种无状态协议

       每次 Web 请求后,客户端和服务器端断开,同时 ASP.NET 引擎释放页面对象。这种架构保证了Web应用程序能够同时响应数千个并发请求而不会导致服务器内存崩溃。但其负面效应是你必须通过其他技术存储 Web 请求之间的信息并在需要的时候获取它们。

 

视图状态

      视图状态是在单个页面中保存信息的第一选择。ASP.NET Web控件也使用试图状态在回发间保存属性值。通过页面内建的 ViewState 属性,你可以把自己的数据放入到视图状态集合中,可以保存的信息类型包括简单数据类型和自定义对象

       和 ASP.NET 中大多数的状态管理类似,视图状态依赖于字典集合。集合中每个项目通过一个唯一的字符串名字进行索引。

ViewState["Counter"] = 1;

如果集合中没有叫做 Counter 的索引项,它将被自动添加。

如果已经存在,它将被替换。

       ViewState 通过键值来读取值,由于需要处理不同的数据类型,它将所有的数据保存为 Object 类型,因此在使用时涉及到数据类型的转换。

int count;
if(VietState["Count"] != null)
{
   count = (int)ViewState["Count"];
}


如果试图查找一个集合中不存在的值,则会得到一个 NullReferenceException 异常,因此需要进行判空操作。

 

视图状态示例

       下面的代码演示了页面如何使用视图状态,它允许用户保存一系列的值,并将其恢复。这段代码使用递归遍历所有的子控件,由于控件ID在页面中是唯一的,所有它被用作视图状态的键值。

{
    protected void btnSave_Click(object sender, EventArgs e)
    {
        SaveAllText(this.Table1.Controls, true);
    }
 
    protected void btnRestore_Click(object sender, EventArgs e)
    {
        RestoreAllText(this.Table1.Controls, true);
    }
 
    private void SaveAllText(ControlCollection controls, bool saveNested)
    {
        foreach (Control control in controls)
        {
            if (control is TextBox)
            {
                ViewState[control.ID] = ((TextBox)control).Text;
            }
 
            // bool类型的 saveNested 参数给方法提供了更大的灵活性
            // 可以控制是否需要递归
            if (control.Controls != null && saveNested)
            {
                SaveAllText(control.Controls, true);
            }
        }
    }
 
    private void RestoreAllText(ControlCollection controls, bool saveNested)
    {
        foreach (Control control in controls)
        {
            if (control is TextBox)
            {
                if (ViewState[control.ID] != null)
                {
                    ((TextBox)control).Text = ViewState[control.ID].ToString();
                }
            }
 
            if (control.Controls != null && saveNested)
            {
                RestoreAllText(control.Controls, true);
            }
        }
    }
}

asp.net 后面打开DIV asp.net viewstate_视图状态

 

在视图状态中保存对象

       在视图状态中保存项目时,ASP.NET 需要将它转换为比特流以便添加到页面的隐藏字段中,这个过程被叫做序列化

       对象在默认情况下不能被序列化,视图将一个不能序列化的对象保存到视图状态中,会得到一条错误信息,为使对象可序列化,需要在类的声明前加上 Serializable 特性

// 指示一个类可以序列化
[Serializable]
public class Customer
{
    public string FirstName;
    public string LastName;
 
    public Customer(string firstName,string lastName)
    {
        FirstName = firstName;
        LastName = lastName;    
    }
}
// 指示一个类可以序列化


       因为 Customer 类现在被标记为可序列化的,所有它可以被保存在视图状态中:

Customer cust = new Customer("Malia", "Carry");
ViewState["CurrentCustomer"] = cust;


       也可以从中读取自定义对象:

Customer cust;
if (ViewState["CurrentCustomer"]!=null)
{
    cust = (Customer)ViewState["CurrentCustomer"];
}
if (ViewState["CurrentCustomer"]!=null)

 

为了让类可序列化,它需要符合下列条件:

  1. 类必须有 Serializable 特性。
  2. 它的任何基类都必须有 Serializable 特性。
  3. 所有成员变量必须为可序列化的数据类型。否则所有不可序列化的数据必须标识 NonSerialized,它们将被简单的忽略。

示例:

public partial class Chapter06_ViewStateTest : System.Web.UI.Page
{
    protected void btnSave_Click(object sender, EventArgs e)
    {
        Dictionary<string, string> dict = new Dictionary<string, string>();
        SaveAllText(this.Table1.Controls, dict, true);
        ViewState["ControlText"] = dict;
    }
 
    protected void btnRestore_Click(object sender, EventArgs e)
    {
        if (ViewState["ControlText"] != null)
        {
            Dictionary<string, string> dict
                = ViewState["ControlText"] as Dictionary<string, string>;
 
            // KeyValuePair 定义可设置或检索的键/值对。
            foreach (KeyValuePair<string, string> item in dict)
            {
                Label1.Text += item.Key + " = " + item.Value + "<br />";
            }
        }
    }
 
    private void SaveAllText(ControlCollection controls, Dictionary<string, string> dict, bool saveNested)
    {
        foreach (Control control in controls)
        {
            if (control is TextBox)
            {
                dict.Add(control.ID, ((TextBox)control).Text);
            }
            if (control.Controls != null && saveNested)
            {
                SaveAllText(control.Controls, dict, true);
            }
        }
    }
}
{


效果:

asp.net 后面打开DIV asp.net viewstate_序列化_02

 

视图状态评估

       视图状态是一个理想的状态管理方式,因为它既不消耗服务器内存也不加强额外限制(如超时)。那么什么原因会迫使你放弃视图状态而采用其他的状态管理方式呢?以下是 3 个可能的原因:

  1. 需要保存业务的核心数据,它们不允许被用户篡改。(聪明的用户可能会在回传请求中修改视图状态信息)
  2. 需要保存被多个页面使用的信息。(这种情况下考虑使用会话状态、cookie、查询字符串)
  3. 需要存储的信息量非常大,并且不希望因此影响页面传送时间。(这种情况下,考虑使用数据库或会话状态)

 

有选择的禁用视图状态

       削减不必要的视图状态是减少页面传送时间的好办法,下面 2 种情况没有必要使用控件的视图状态:

  1. 控件从来不会变化(例如一个静态文字的按钮不需要使用视图状态)
  2. 控件在每次回传中被重新填充

       将某个控件的 EnableViewState 属性设为 false 后,就可以关闭它的视图状态。而将页面的 EnableViewState 设为 false 后,可以关闭整个页面及其所有控件的视图状态(设置 Page 指令中的 EnableViewState 特性可以达到同样效果)。还可以在 web.config 文件里把 <pages> 元素的 enableViewState 特性设置为 false 来禁用网站所有页面的视图状态。

 

       可以关闭某个页面的视图状态,但通过对特定控件显式起用视图状态,从而达到选择性的覆盖页面设置!这项技术室 ASP.NET 4 新增的,特别受热心于把页面视图状态减小到最少的开发人员的欢迎。为了达到这一目的,需要用到另一个叫做 ViewStateMode 的属性。

       ViewStateMode 可以应用到所有控件和页面,取值有以下 3 个:

  1. Enable:只要 EnableViewState 属性启用了视图状态,视图状态就可以工作。
  2. Disabled:该控件的视图状态不起作用,但该控件的子控件可以覆盖这个值。
  3. Inherit:控件继承该控件的容器的 ViewStateMode 值。(默认选项)

       为了有选择的使用视图状态,现在可以把页面的 ViewStateMode 设为 Disabled 来关闭页面级视图状态,需要使用视图状态的控件则单独设置该属性为 Enable。请注意,我们并没有把 EnableViewState 设置为 false ,如果这么做的话,ASP.NET 会完全关闭页面的视图状态,没有控件可以再起用它

       这个模型有点笨拙,但当视图状态的大小构成问题时它确实有用。唯一的缺点是你必须记得为那些具有需要持久化的动态值的控件或者必须依赖于视图状态才能正确工作的控件显式起用视图状态。

 

视图状态安全

<input type="hidden"  name="__VIEWSTATE"  id="__VIEWSTATE"  value="fakIvy3d8tjpX6spp/o260Nau17ykR" />

       视图状态信息以 Base64 编码的单个字符串形式保存,由于这些值并没有格式化为明文的形式,所有很多 ASP.NET 程序员误以为他们的视图状态数据时被加密的,但事实并非如此,恶意的用户可以在数秒内逆向的设计这个字符串从而查到你的视图状态数据。

       有两个方法可以让视图状态更加安全:

  1. 使用散列码保证视图状态信息不被篡改(散列码是一种强校验码)。
  2. 启用视图状态加密

注1:

       散列码默认就是启用的,你不必作任何多余的设置

注2:

       启用加密可以在 Page 指令中或 web.config 文件中设置

<%@ Page ViewStateEncryptionMode="Always" />
<pages viewStateEncryptionMode="Always" />

 

       加密设定有3个选项:

  1. Always:总是加密
  2. Never:从不加密
  3. Auto:总在控件要求时加密(默认选项)

       当控件要求加密时,会调用 Page.RegisterRequiresViewStateEncryption()方法来启用加密。但控件并没有绝对的控制力,如果页面加密模式是 Never,那么控件即便调用了加密方法,也不会有任何作用

 

       非必要时不要加密视图状态数据。加密会导致性能下降,因为每次回发时 Web 服务器都需要执行加密和解密。