ViewState机制由浅入深

1         ViewState机制是什么?

ViewState机制是asp.net中对同一个Page的多次请求(PostBack)之间维持Page及控件状态的一种机制。在WebForm中每次请求完,Page对象都会被释放,对同一个Page的多次请求之间的状态信息,如何进行维护呢?WebForm中,每次请求都会存在客户端和服务器之间的一个交互。如果请求完成之后将一些信息传回到客户端,下次请求的时候客户端再将这些状态信息提交给服务器,服务器端对这些信息使用和处理,再将这些信息传回给客户端。这样是不是就可以对同一个Page的多次请求(PostBack)之间维持状态了。对这就是ViewState的基本工作模式。ViewState的设计目的主要就是为了将必要的信息持久化在页面中。这样通过ViewState在页面回传的过程中保存状态值,使原本没有“记忆”的Http协议变得有“记忆”起来。

2         ViewState机制如何工作?

下面我们看看ViewState机制是如何具体的工作的。

2.1 客户端:

我们先从客户端看起,在客户端的HTML源代码中我们可以看到下面的代码

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"

value="/wEPDwULLTE0MTAzNDUwNThkZKr77J2uy7fatyBou8PocG80X4Jt" />

这个就是ViewState在客户端的保存形式,它保存在一个ID为__VIEWSTATE的Hidden中,它的Value是使用Base64编码后的字符串。这个字符串实际上是一个对象(Pair类型)序列化之后的结果。这个对象保存了整个页面的控件树的ViewState。可以使用一些工具将这个字符串进行解码查看其内容,比如ViewStateDecoder,ViewStateAnalyzer。

2.2 服务器端:

在服务器端和ViewState机制密切相关的有三个类Page,Control,StateBag。他们3者的关系如下图所示:


图1

Page继承自Control,Control和StateBag是聚合关系,在Control中有一个StateBag的实例ViewState。这三个类互相协作完成ViewState机制的大概过程如下。Page对客户端请求进行处理,在处理的过程中先是将客户端提交的_VIEWSTATE反序列化为对象,调用Control的相关方法给所有的控件装载数据,这些数据是上次请求结束后控件的状态数据。在之后的一些事件中这些状态数据可能被修改。在请求结束之前调用Control的相关方法得到所有控件的被修改过的状态数据,之后Page将其进行序列化,并返回给客户端。在Control中又具体调用StateBag类的方法完成状态数据的加载和保存。

2.2.1   Page中的处理


图2 Page生命周期

1)     InitRecursive

在Page的生命周期中有3处与ViewState相关,在初始化阶段调用Control. InitRecursive,它递归对所有的控件进行初始化,其中调用了Control.TrackViewState。TrackViewState中打开跟踪ViewState开关。

2)     LoadAllState

在初始化完成之后会调用Page.LoadAllState,LoadAllState只有在PostBack的时候才会执行,它的主要功能是将从页面传递来的__VIEWSTATE的值反序列化为Pair类型的对象,然后将这个对象中存储的ViewState的值加载到Page及所有控件中。实际上LoadAllState加载了ControlState(控件状态)及ViewState(视图状态),本文主要是讨论ViewState,对ControlState部分的处理不进行描述。

LoadAllState中主要有两步:Page.LoadPageStateFromPersistenceMedium和Control.LoadViewStateRecursive。

在LoadPageStateFromPersistenceMedium中发生了如下的调用层次

Page.LoadPageStateFromPersistenceMedium

è    HiddenFieldPageStatePersister.Load

è    ObjectStateFormatter.Deserialize

以上完成的功能是将客户端提交的_VIEWSTATE反序列化为一个类型为Pair的对象pair。

Control.LoadViewStateRecursive中将递归加载控件的ViewState,具体在下面进行讲解。

3)     SaveAllState

SaveAllState它的操作和LoadAllState相反。SaveAllState中主要有两步Control.SaveViewStateRecursive及Page SavePageStateToPersistenceMedium。

Control.SaveViewStateRecursive中将所有控件的ViewState属性递归加载到一个Pair对象中,具体实现细节在下面讲解。

Page SavePageStateToPersistenceMedium中发生如下的调用关系。

Page SavePageStateToPersistenceMedium

è    HiddenFieldPageStatePersister.Save

è    ObjectStateFormatter.Serialize

将Control.SaveViewStateRecursive生成的对象序列化为一个字符串,并赋值给Page.ClientState属性。

在Render阶段发生如下的调用关系:

HtmlForm.RenderChildren

è    Page.BeginFormRender

è    Page.RenderViewStateFields

最终将ClientState属性中的值写入到HTML页面的_VIEWSTATE中。

在Control.InitRecursive中打开跟踪开关,打算对ViewState的值进行跟踪,在Page.LoadAllState中将客户端提交的__VIEWSTATE的值装载到各个控件的ViewState中,在Page.SaveAllState中将发生变化的ViewState序列化为一个字符串,在Render阶段发送回客户端。

4)     ViewState序列化与反序列化

PageStatePersister 是一个抽象类,是表示将ViewState信息序列化及反序列化机制的基类。在Page.LoadPageStateFromPersistenceMedium中示意代码如下:

protected internal virtual object LoadPageStateFromPersistenceMedium()

{

          PageStatePersister pageStatePersister = this.PageStatePersister;

          pageStatePersister.Load();

          return new Pair(pageStatePersister.ControlState, pageStatePersister.ViewState);

}

在Page.SavePageStateToPersistenceMedium中的示意代码如下:

protected internal virtual void SavePageStateToPersistenceMedium(object state)

{

        PageStatePersister pageStatePersister = this.PageStatePersister;

        Pair pair = (Pair) state;

        pageStatePersister.ControlState = pair.First;

        pageStatePersister.ViewState = pair.Second;

        pageStatePersister.Save();

}

在Asp.net2.0中实现PageStatePersister这个抽象类,具体提供持久化机制的类是HiddenFieldPageStatePersister。它实现了Load和Save两个方法,Load时将__VIEWSTATE反序列化为一个Pair对象,Save时将Pair对象序列化为一个字符串赋值给Page.ClientState。HiddenFieldPageStatePersister中采用的格式器是ObjectStateFormatter,其实现string Serialize(object state),和object Deserialize(string serializedState)这两个方法,从而实现对Pair对象的序列化和反序列化。

public string Serialize(object state)中的示意代码如下:

MemoryStream memoryStream = GetMemoryStream();

Serialize(memoryStream, state);

byte[] buf = memoryStream.GetBuffer();

if (RequiresViewStateEncryptionInternal)

{

 buf = MachineKeySection.EncryptOrDecryptData(true, buf, this.GetMacKeyModifier(), 0, length);

 length = buf.Length;

}

else if (EnableViewStateMac)

{

buf = MachineKeySection.GetEncodedData(buf, this.GetMacKeyModifier(), 0, ref length);

}

return Convert.ToBase64String(buf, 0, length);

将state序列化为内存流,在将其转换为字节流。如果需要加密则对其进行加密处理,否则需要Mac则进行Mac处理。最后将字节流进行Base64编码转换为字符串。

public object Deserialize(string serializedState) 中的示意代码如下:

byte[] buf = Convert.FromBase64String(serializedState);

int length = buf.Length;

if (ContainsEncryptedViewState)

{

buf = MachineKeySection.EncryptOrDecryptData(false, buf, this.GetMacKeyModifier(), 0, length);

 length = buf.Length;

}

else if (EnableViewStateMac)

{

buf = MachineKeySection.GetDecodedData(buf, this.GetMacKeyModifier(), 0, length, ref length);

}

MemoryStream memoryStream = GetMemoryStream();

memoryStream.Write(buf, 0, length);

return this.Deserialize(memoryStream);

将字符串进行Base64解码为字节流,如果需要解密则进行解密处理,否则需要进行需要Mac则进行Mac处理,将字节流转换为内存流,进行反序列化返回Pair对象。

 

 

 

2.2.2   Pair类及ViewState的存储

Page及所有控件的ViewState、ControlState都是存储在Pair类的实例中,了解Pair类及ViewState如何存储在Pair类中很重要。Pair定义的System.Web.UI中具体定义如下:

public sealed class Pair

{

    public object First;

    public object Second;

    public Pair();

    public Pair(object x, object y);

}

可以看出Pair类是用作存储两个相关对象的基本结构。Pair是一个工具类,使用它很容易形成一个树的数据结构。将__VIEWSTATE反序列化后就是一个Pair对象。这个对象即保存了控件之间的父子关系,也保存了ViewState信息。ViewState中的Key/Value对被转换为一个ArrayList对象保存在Pair对象中。下面我们来看看它是如何存储的。


图3

图3是对Pair的一种图式表示方式,左边表示First对象,右边表示Second对象,根据这种图式方式,图4是利用Pair对象存储ViewState的结构,对于ControlState没有进行具体的展开。从savedState(Pair)开始采用了递归的存储方式,将所有控件的ViewState及关系存储起来,在Pair对象的First中存储ViewState数据,在Second中存储子控件的信息。

图4

2.2.3   Control中的处理

Page类继承自Control类,在实现ViewState机制的时候Page类中主要是涉及序列化和反序列化的工作,ViewState的保存、装载等功能都在Control类中实现由Page类来继承,控件也都继承自Control类,下面对Control类中和ViewState机制相关的属性、方法进行介绍。
1)     ViewState属性
ViewState是Page及控件的一个属性,这个属性并不是在Page或控件中定义,它定义在System.Web.UI.Control类中。它的声明如下:

protected virtual StateBag ViewState{get}

由于所有服务器端的控件,用户自定义控件,及Page类都是继承自Control类,所以它们都会都具有一个protected的ViewState属性。这个ViewState属性在ViewState机制中很重要,它用来存储控件需要记忆的一些数据。这些数据主要有两类,一类是需要记忆的控件的属性。另外一类是我们想利用ViewState机制来记住的一些数据,比如我们在Web窗体中写如下的代码:

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!this.IsPostBack)

        {

            this.ViewState["Test"] = 0;

        }

        else

        {

            this.ViewState["Test"]=int.Parse(this.ViewState["Test"].ToString()) + 1;

        }

    }
因为我们创建的Web窗体都是继承自Page类,所以在Web窗体中能否访问Page类的protected的ViewState属性。我们可以通过ViewState属性利用ViewState机制,让它帮助我们记住一些信息。
ViewState属性的主要作用还是用来记住一些控件的属性值。ViewState属性和控件的其他属性有什么样的联系,才能够利用ViewState属性来记住他们呢?下面用Button类的Text属性举例来说明,Text属性和ViewState属性是什么关系。Button.Text的定义如下:

public string Text

{

    get

    {

        string str = (string) this.ViewState["Text"];

        if (str != null)

        {

            return str;

        }

        return string.Empty;

    }

    set

    {

        this.ViewState["Text"] = value;

    }

}

通过上面的代码我们可以看出Button.Text属性的值实际上和ViewState中Key为“Text”的Value是关联的。当然Button类的其他属性也是类似的方式,只是对应ViewState中Key不同而已。这样只要ViewState的值能够被记忆,那么Button类的属性也就能够被记忆住了。记忆ViewState的方法就是在Page. SaveAllState中将所有控件的ViewState属性生成一个对象(这是一个特殊的对象,它是一个树状态的存储了所有控件的ViewState属性),然后将这个对象序列化为字符串,发送到客户端。在下次请求的时候,将这个字符串发送给服务器,在Page.LoadAllState中将这个字符串反序列化为一个对象,将这个对象中存储的各个控件的ViewState属性,加载给各个控件的ViewState属性。当我们访问控件的属性的时候就实现了对控件属性的记忆,之前设置的控件属性没有丢失。
2)     LoadViewStateByID属性

LoadViewStateByID的声明如下:

protected bool LoadViewStateByID{get}

LoadViewStateByID获取一个值,该值指示控件是否通过ID方式加载其视图状态。默认情况下是通过索引方式加载视图状态,索引方式是依赖子控件在父控件的Controls集合中位置。ID方式会根据ID查找控件,效率比较低些,但是有些情形必须使用这种方式比如延迟创建子控件时。

3)     EnableViewState属性
EnableViewState是一个bool类型的属性,来决定当前控件的ViewState机制是否可用。其声明如下:

public virtual bool EnableViewState{get,set}

4)     LoadViewStateRecursive
LoadViewStateRecursive的声明如下:
internalvoid LoadViewStateRecursive(object savedState)
Page.LoadAllState中调用Control.LoadViewStateRecursive,传入的参数savedState是一个Pair类型的对象,Pair.Fisrt中保存当前控件的ViewState,Pair.Second中保存子控件的ViewState。对于EnableViewState为true时,先通过调用Control.LoadViewState(savedState.First)装载当前控件的ViewState。之后根据LoadViewStateByID属性,装载子控件的ViewState。如果LoadViewStateByID属性为true调用Control.LoadChildViewStateByID(savedState.Second),否则调用
Control.LoadChildViewStateByIndex(savedState.Second)。savedState.Second是一个ArrayList类型的对象。

Control.LoadViewState的声明如下:

protected virtual void LoadViewState(object savedState)
在Control.LoadViewState中并没有自己实现装载当前控件的ViewState,而是通过ViewState.LoadViewState(savedState)来实现,下面会介绍StateBag的LoadViewState方法。
LoadChildViewStateByID的声明如下:
internal void LoadChildViewStateByID(ArrayList childState)
参数childState是ArrayList类型,childState中存储了当前控件的所有子控件的信息。它的格式是首先是一个控件的ID,其后是这个控件的Pair对象。对childState进行循环,循环中取得控件的ID,根据ID找到控件调用这个控件的LoadViewStateRecursive方法。示意代码如下:

for (int i = 0; i < count; i += 2)

    {

        string id = (string) childState[i];

        object savedState = childState[i + 1];

       Control control = this.FindControl(id);

        control.LoadViewStateRecursive(savedState);

    }

LoadChildViewStateByIndex的声明如下:

internal void LoadChildViewStateByIndex(ArrayList childState)

childState的存储格式是首先是一个控件的索引,其后是这个控件的Pair对象。根据索引访问访问这个控件,调用其LoadViewStateRecursive方法,示例代码如下:

    for (int i = 0; i < num2; i += 2)

    {

        int num4 = (int) childState[i];

        object savedState = childState[i + 1];

        controls[num4].LoadViewStateRecursive(savedState);

     }

5)     LoadViewState

LoadViewState中直接调用ViewState的LoadViewState方法进行ViewState的装载,示意代码如下:

this.ViewState.LoadViewState(savedState);

6)     SaveViewStateRecursive
SaveViewStateRecursive的声明如下:

internal object SaveViewStateRecursive()

对于EnableViewState为true时,先调用Control.SaveViewState返回一个包含当前控件的ViewState信息的ArrayList类型对象x。之后对子控件进行递归处理获得一个ArrayList类型的对象z。它的格式是ID(String),savedState(Pair)或者Index(int)savedState(Pair)。最后创建一个Pair对象Pair(x, z)。示意代码如下:

object x = this.SaveViewState();

ArrayList z = null;

ControlCollection controls = this._occasionalFields.Controls;

int count = controls.Count;

bool loadViewStateByID = this.LoadViewStateByID;

for (int i = 0; i < count; i++)

{

          Control control = controls[i];

          object obj4 = control.SaveViewStateRecursive();

          if (loadViewStateByID)

                    z.Add(control.ID);

          else

                              z.Add(i);

          z.Add(obj4);

}

return new Pair(x, z);

7)     SaveViewState

SaveViewState中直接调用ViewState的SaveViewState方法进行ViewState的保存,示意代码如下:

return this.ViewState.SaveViewState();

8)     TrackViewState

在Control.InitRecursive中会调用Control.TrackViewState,因为Control.InitRecursive是被递归调用的,所以每个控件的TrackViewState都会在初始化阶段被调用到。Control.TrackViewState中之间调用ViewState的TrackViewState方法,示意代码如下:

this.ViewState.TrackViewState();

 

 

 

 

2.2.4   StateBag

ViewState是控件的一个属性,用来使用控件具有记忆功能。在前边的讲述中,我们可以看到控件的一些属性通过使用ViewState能够恢复原来的值,保存本次的值,在Control类中很多方法的实现也是直接调用了ViewState的方法。ViewState的类型是StateBag,下面我们就了解一下在StateBag中是如何实现这些功能的。StateBag定义在System.Web.UI中声明如下:

public sealed class StateBag : IStateManager, IDictionary, ICollection, IEnumerable

StateBag类可以理解为是一个具有状态管理功能的字典,因为它实现了IStateManager, IDictionary 这两个接口。StateBag类可以象字典那样保存Key/Value对,其中Key是字符串而Value是对象。下面是一个使用StateBag的例子。

    protected void Button2_Click(object sender, EventArgs e)

    {

        StateBag TestSB = new StateBag();

        TestSB["b"] = "bbbbb";

        TextBox1.Text = TestSB["b"].ToString();

}

在上面的例子中使用StateBag保存一个Key为“b”,其值为“bbbbb”的Key/Value对。ViewState属性也是StateBag的一个实例,当然也就可以象上面那样使用。在ViewState中保存了很多的Key/Value对(键值对),这些Key/Value对用来保存控件的属性,这些Key/Value对是有ASP.Net来维护的。当然我们也可以增加一些自己的Key/Value对,来保存一些信息。

StateBag还实现System.Web.UI.IStateManager接口,这样它具有状态管理功能。下面对StateBag如何提供状态管理功能进行说明。

1)     StateItem

StateBag中保存Key/Value对,Key是String类型,Value是Object类型。但是在StateBag内部保存Value不是Object类型,而是将Object类型转换为StateItem类型然后保存,从StateBag中取出的时候再将StateItem类型转换为Object类型,也就是说StateBag中的Key/Value对实际上是String/StateItem类型。转换过程是在StateBag内部实现客户感觉不到。StateItem的声明如下:

public sealed class StateItem

{

    internal StateItem(object initialValue);

    public bool IsDirty { get; set; }

    public object Value { get; set; }

}

通过上面的代码我们可以看出实际上多了一个IsDirty属性,来标记当前的Value是否已经被修改过。

2)     Add

Add方法是将传入的Key,Value保存到字典中,并处理IsDirty属性。在StateBag.LoadViewState方法中会调用Add方法。其示意代码如下:

public StateItem Add(string key, object value)

{

    StateItem item = this.bag[key] as StateItem;

    if (item == null)

    {

        if ((value != null) || this.marked)

        {

            item = new StateItem(value);

            this.bag.Add(key, item);

        }

    }

    else

    {

        item.Value = value;

    }

    if ((item != null) && this.marked)

    {

        item.IsDirty = true;

    }

    return item;

}

虽然函数的名称是Add,其实也包括了更新。如果当前的项在字典中不存在则新增,否则更新。新增时新建一个StateItem类型的对象item,将Key和item增加到字典中。如果Item不为null,并且跟踪状态标记为true,则item的IsDirty为true。什么情况下会调用Add方法呢?主要有两种情形一种是StateBag.LoadViewState,在下面会具体介绍到。还有一种情况就是对控件的属性赋值的时候,比如Button.Text=”button”,此时会调用Text属性的Set,在Set中执行的代码this.ViewState["Text"] = value,这个代码实际上执行this.ViewState.Add("Text",value)。

3)     LoadViewState

还原以前保存的ViewState。将传入的ArrayList对象加载到字典中。示意代码如下:

internal void LoadViewState(object state)
{
        ArrayList list = (ArrayList) state;
        for (int i = 0; i < list.Count; i += 2)
        {
            string key = ((IndexedString) list[i]).Value;
            object obj2 = list[i + 1];
            this.Add(key, obj2);
        }

}

我们知道在初始化阶段StateBag.TrackViewState都已经被调用过了,也就是说marked为true了,这样在StateBag.LoadViewState中调用Add方法第一次加载完ViewState的数据后,所有的StateItem的IsDirty属性都是true。

4)     SaveViewState

将字典中已经修改过的Key/Value存放在一个ArrayList对象中返回。

internal object SaveViewState()
{
   ArrayList list = null;
   IDictionaryEnumerator enumerator = this.bag.GetEnumerator();
   while (enumerator.MoveNext())
   {
            StateItem item = (StateItem) enumerator.Value;
            if (item.IsDirty)
            {
                list.Add(new IndexedString((string) enumerator.Key));
                list.Add(item.Value);
            }
        }
    }
    return list;

}

在SaveViewState中只会将字典中item.IsDirty=true的项目返回,哪些项的IsDirty=true呢?通过上面对Add方法及LoadViewState的分析我们知道,当我们第一次设置了控件的某个属性后会调用Add方法,这个属性对应的StateItem的属性IsDirty会被设置为true,在SaveViewState时会保存这个item。在下次请求时在LoadViewState中也会将IsDirty设置为true,在SaveViewState是会保存这个item。总之只要控件属性的被修改过和默认值不一致都会一直被保存,即使这个属性的值仅仅被修改过一次,之后保存不不变,也会在多次PostBack之间保存起来。

5)     TrackViewState

在TrackViewState方法中,设置跟踪标记为True,其目的就是开始状态跟踪了。示意代码如下:

internal void TrackViewState()

{

    this.marked = true;

}

3         ViewState与ControlState

ControlState是一个自定义的状态保持机制,也就是说保持状态的机制需要开发人员自己去完成,而不像ViewState,它有自己默认的状态保持机制。既然已经有了ViewState为什么还需要ControlState呢?因为ViewState是可以被禁用的,而ControlState却不能被禁用,对于有些必须保存的信息,就可以使用ControlState。ControlState的实现思路基本上与ViewState类似,ControlState需要保存的信息也被序列化后保存在__VIEWSTATE中。使用ControlState,需要在OnInit 方法中调用 RegisterRequiresControlState向页面注册,而且要重写SaveAdapterControlState,LoadAdapterControlState这两个方法自己,实现要保存什么,怎样保存。

4         ViewState的使用

4.1 ViewState的优缺点

使用ViewState首先要了解ViewState与其他的保持状态机制相比有什么优缺点。

使用ViewState具有以下优点:

一、耗费的服务器资源较少(与Application、Session相比)。因为,视图状态数据都写入了客户端计算机中。

二、易于维护。默认情况下,DotNet系统自动启用对状态数据的维护。

三、因为它不使用服务器资源、不会超时,并且适用于任何浏览器。

使用视图状态具有以下缺点:

一、性能问题。由于视图状态存储在页本身,因此如果存储较大的值,用户显示页和发送页时的速度仍然可能减慢。ViewState 增加了发送到浏览器的页面的大小,同时也增加了回传的窗体的大小,因此不适合存储大量数据

二、设备限制。移动设备可能没有足够的内存容量来存储大量的视图状态数据。

三、潜在的安全风险。视图状态存储在页上的一个或多个隐藏域中。虽然视图状态以哈希格式存储数据,但它可以被篡改。如果直接查看页输出源,可以看到隐藏域中的信息,尽管 ViewState 数据已被编码,并且可以选择对其进行加密,但始终不将数据发送到客户端才是最安全的。

4.2 ViewState安全性

ViewState将一些信息保存在客户端,而且默认情况下客户端的数据仅仅是进行了Base64编码了,很容易被看到原文。当然ViewState还有一些安全性措施来改善这一点。ViewState 安全性对于处理和呈现 ASP.NET 页面所需的时间有直接的影响。简单地说,安全性越高,速度越慢。因此如果不需要,不要为 ViewState 添加安全性。

4.2.1   MAC

MAC是message authentication check的简写即消息验证检查。MAC可以防止ViewState数据被篡改。可以通过设置EnableViewStateMAC="true"属性来启用消息验证检查。可以在页面级别上设置 EnableViewStateMAC,也可以在应用程序级别上设置。有一点需要特别说明的是在MSDN中说EnableViewStateMAC的默认值是Flase,但是实际上发现EnableViewStateMAC的默认值是True,这一点大家可以Page_Load中输出EnableViewStateMAC属性来验证。也就是说默认情况下都是对ViewState进行防篡改处理的。下面的描述结合“2.2.1Page中的处理”中的“4)ViewState序列化与反序列化”进行理解。

ObjectStateFormatter中进行序列化的时候如果EnableViewStateMAC="true",则在MachineKeySection.GetEncodedData根据字节流buf计算出一个20位的字节表示Hash值,增加在buf字节流的后边,形成一个新的字节流。

ObjectStateFormatter中进行反序列化的时候如果EnableViewStateMAC="true",则在MachineKeySection.GetDecodedData中取buf的前(length-20)位计算出一个20位的字节表示Hash值,将这个Hash值与buf的最后20位进行比较,如果不一直则说明ViewState被篡改了则抛出异常。

默认情况下,.NET框架使用SHA1算法来生成ViewState散列代码。此外,也可以通过在machine.config文件中设置<machineKey>来选择 MD5 算法,如下所示:<machineKey validation="MD5" />。MD5算法的性能要比SHA1算法好一些,但是同样不够安全。

4.2.2加密

默认情况下__VIEWSTATE中存储的值仅仅仅进行了Base64编码,并没有进行加密,如果ViewState中有一些敏感信息需要增加安全性,我们也可以对ViewState进行加密。我们可以设置ViewStateEncryptionMode的值来决定是否加密,其值是“Auto”,“Always”,“Never”,默认值是“Auto”。“Always”表示进行加密,“Never”表示不进行加密,“Auto”时如果调用了RegisterRequiresViewStateEncryption方法后则进行加密。ViewStateEncryptionMode属性不能在代码中设置page指令或者配置文件中使用。

4.2.3设置ViewStateUserKey

设置 ViewStateUserKey 属性有助于防止您的应用程序受到恶意用户的点击式攻击。必须在页处理的 Page_Init 阶段设置此属性。具体的信息可以MSDN中的说明,链接如下:

http://msdn2.microsoft.com/zh-cn/library/ms972969.aspx

4.3 ViewState的禁用

因为ViewState会一定程度上影响性能所以在不需要的时候禁用 ViewState。默认情况下 ViewState 将被启用,并且是由每个控件(而非页面开发人员)来决定存储在 ViewState 中的内容。有时,这一信息对应用程序并没有什么用处。尽管也没什么害处,但却会明显增加发送到浏览器的页面的大小。因此如果不需要使用 ViewState,最好还是将它关闭,特别是当 ViewState 很大的时候。通过将对象的EnableViewState属性设置为False禁用ViewState。可以针对单个控件、整个页面或整个应用程序禁用ViewState,如下所示:

每个控件(在标记上)<asp:datagrid EnableViewState="false" ?/>

每个页面(在指令中) <%@ Page EnableViewState="False" ?%>

每个应用程序(在 web.config 中) <Pages EnableViewState="false" ?/>

以下情况将不再需要ViewState:(1)控件未定义服务器端事件(这时的控件事件均为客户端事件且不参加回送的);(2)控件没有动态的或数据绑定的属性值。

4.4 ViewState优化

ViewState优缺点并存,有些人支持使用,有些人反对使用,也有人提出了对ViewState的优化,主要是压缩ViewState减少传输量及修改ViewState的存储位置。具体可以参考一下的链接。

压缩:http://www.cnblogs.com/mack/archive/2005/07/27/201411.html

修改存储位置:http://czhenq.cnblogs.com/archive/2006/04/03/365807.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ASP.NET ViewState 是一种用于在 Web 应用程序中跨请求存储数据的机制。以下是一个使用 ViewState 的示例: 假设您有一个页面,其中包含一个文本框和一个按钮。用户在文本框中输入一些文本,然后单击按钮。在单击按钮时,将在服务器端处理程序中使用 ViewState 存储文本框中的值,并在页面上显示它。 以下是一个简单的 ASP.NET 页面代码示例,它演示了如何使用 ViewState 存储和检索文本框中的值: ```html <%@ Page Language="C#" %> <!DOCTYPE html> <html> <head runat="server"> <title>ViewState Example</title> </head> <body> <form id="form1" runat="server"> <div> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <br /> <asp:Button ID="Button1" runat="server" Text="Save" OnClick="Button1_Click" /> <br /> <asp:Label ID="Label1" runat="server"></asp:Label> </div> </form> </body> </html> ``` 在按钮单击事件处理程序中,我们将文本框中的值存储ViewState 中,并将其显示在页面上: ```csharp protected void Button1_Click(object sender, EventArgs e) { string text = TextBox1.Text; ViewState["myText"] = text; Label1.Text = "Text saved: " + text; } ``` 在页面加载事件处理程序中,我们检索存储ViewState 中的值,并将其显示在页面上: ```csharp protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { if (ViewState["myText"] != null) { string text = (string)ViewState["myText"]; Label1.Text = "Text retrieved: " + text; } } } ``` 通过这种方式,我们可以在页面上保留用户在文本框中输入的值,即使用户单击其他按钮或导航到其他页面。请注意,ViewState 可能会增加页面大小,并增加网络传输时间。因此,我们应该谨慎使用 ViewState,并仅在必要时使用它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值