ASP.Net ViewState的实现
ViewState是.Net中提出的状态保存的一种新途径(实际上也是老瓶装新酒);我们知道,传统的Web程序保存状态的方式有这样几种:
1、Application 这是Web应用程序生命期中的全局保存区,保存在Application中的数据是全局有效的;在Asp.Net中,有一个应用程序池,其中保存了数个(或数十个)应用程序实例,每一次请求都会从池中取一个实例来处理请求,在请求完毕之前,这个实例不会接受其他请求;这就出现一个问题,同一时间可能存在多个应用程序,也就是多个线程,这些线程都存在访问Application的可能,所以在对Application中的对象进行处理的时候需要考虑线程同步的问题;实际上Application对象内部实现了一个线程锁,调用它本身的Add、Remove等方法的时候会自动调用加锁和解锁的操作,但是出于性能考虑,对于直接通过索引器或其他方式得到其中的对象并进行操作的过程,Application并没有自动处理线程同步,需要利用下列类似的代码来处理:
Application.Lock();
((int)Application["Count"])++;
Application.Unlock();
值得注意的是,调用了Lock之后,如果没有显示的调用Unlock,那么在这个请求结束的时候,Application对象会自动解锁,这样防止了造成死锁的问题,但是为了代码的健壮性,调用完Lock并且修改完毕应该立即的调用Unlock方法。
Application对象本质上就是一个Hash表,按照键值存放了对象,由于对象是全局并且存放在服务器,并且存在多线程同时访问,所以,Application里面存放的应该是访问较多,修改较少并且是全局至少大部分功能会使用的数据,例如计数器或者数据库连接串等。
2、Session 在Asp.Net内部,有一个StateApplication来管理Session,实际上就是一个辅助进程,处理Session到期、创建的特殊请求,在收到每一次请求的时候,辅助进程就会调用状态服务器(可以通过Web.config设置不同的状态服务器)来获取Session,如果没有对应该SessionId的Session,则会新建一个,然后绑定到上下文中(HttpContext);与Asp不同的是,Session的状态服务器有多种,目前在Asp.Net内部实现了三种:
1) InProcStateClientManager 这是传统的Session保存方式,但是还是有些细微差别
2) SqlStateClientManager 这是将Session保存到数据库方式
3) OutOfProcStateClientManager 这是将Session保存到进程外的方式
Asp.Net的Session机制有一个特点,就是处理Session的辅助进程与保存Session的状态服务器是分开的,按照MSDN的说法,有下列好处:
“因为用于会话状态的内存不在 ASP.NET 辅助进程中,所以可以实现从应用程序故障的恢复。”
“因为所有状态与辅助进程不存储在一起,您可以干净地跨多个进程对应用程序进行分区。这种分区可以显著地提高多个进程的计算机上应用程序的可用性和可缩放性。”
“因为所有状态与辅助进程不存储在一起,所以您可以跨运行于多个计算机上的多个辅助进程对应用程序进行分区。”
Asp.Net的Session机制个人观点,感觉灵活性比较好,内部实现也比较巧妙,但是实际上因为没有做过多的测试,所以应用上会不会像它说的那么美好,不敢打包票。有机会,我会单独写篇文章来深入的探讨Asp.Net 内部的Session机制。
3、Cookie 这个没甚么好说,实际上Asp.Net与Asp的Cookie没甚么分别,也许这项技术毁誉参半,而且比较依赖客户机实现,MS也没什么改进的。
4、ViewState 这是我们今天重点讨论的;实际上ViewState并不神秘,就是一个Hidden字段,但是它是服务器控件状态保存的基础;不熟悉的朋友可以用IE查看Html源码,找到一个名为"__VIEWSTATE"的Hidden字段,其中有一大堆乱七八糟的字符,这就是页面的ViewState。
做过Web程序的人可能都有这种痛苦的体会,有时候为了处理页面上面比较复杂的功能,常常会加很多Hidden,然后在服务器端用一大堆判断来分析目前的状态,写起来烦人,写完了代码更是难看;实际上,ViewState就是帮我们系统的实现了保存控件状态的功能,服务器端控件能够在多次请求间保存状态也全靠它。
好,介绍就到这里,今天我们不是讨论ViewState的使用,而是从内部来探探这个东西的本质。
我们首先建一个测试的页面:
<%@ Page language="c#" Codebehind="ViewStateTest.aspx.cs" AutoEventWireup="false" Inherits="CsdnTest.ViewStateTest" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
<head>
<title>ViewStateTest</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body>
<form id="ViewStateTest" method="post" runat="server">
<asp:Button ID="btnPostBack" Runat="server" Text="Post Back" Width="85px"></asp:Button>
<br/>
<asp:CheckBox ID="chkTest" Runat="server" Text="This is a check box"></asp:CheckBox>
</form>
</body>
</html>
这是用Vs.Net设计出来的一个简单的页面,里面包含了一个服务器端的按钮和一个CheckBox,然后我们在服务器端响应按钮的事件:
private void btnPostBack_Click(object sender, System.EventArgs e)
{
[1] Response.Write( "ViewState :"+Request.Params["__VIEWSTATE"]+"<br/>" );
[2] string decodeValue = Encoding.UTF8.GetString( Convert.FromBase64String( Request.Params["__VIEWSTATE"] ) );
[3] Response.Write( "ViewState decode :"+decodeValue+"<br/>" );
[4] object viewstate = (new LosFormatter()).Deserialize( Request.Params["__VIEWSTATE"] );
[5] Response.Write( "ViewState Object :"+viewstate.GetType().Name );
}
为了方便看,我加上了行号;第一行我们把ViewState的值打出来,第二行是什么呢?实际上ViewState保存到客户端的一串字符串就是内部的ViewState通过某种方式序列化之后再经过Base64编码得来的,所以我们把Base64编码的字符串反编码一次再打出来;至于第四行,我先不说,先看执行结果:
运行之后,页面上什么都没有,除了按钮和CheckBox(废话 :)),我们点击按钮,然后结果如下:
[A] ViewState :dDwxMjU2MDI5MTA3OztsPGNoa1Rlc3Q7Pj6Gg0Qzm+7gacYWcy0hnRCT9toOdA==
[B] ViewState decode:t<1256029107;;l>D3i s-! t
[C] ViewState Object :Triplet
然后我们来分析这个结果,A中显示的就是ViewState传到客户端的值,B中显示的是通过Base64反编码之后的值,从这里面好像还是看不出什么,C中出现了一个:Triplet ?这是什么呢,我们回到上面的代码:
object viewstate = (new LosFormatter()).Deserialize( Request.Params["__VIEWSTATE"] );
注意我们使用了一个LosFormatter类,实际上这个类就是Asp.Net内部为ViewState提供序列化的类,它有两个方法,一个是Serialize,就是序列化一个对象,一个是Deserialize,是反序列化,我们这里使用了反序列化的方法来把ViewState直接反序列化成一个对象,然后把这个对象的类型打出来,这个对象就是:Triplet类型,实际上Asp.Net中页面保存的ViewState就是这个类型,我们先分析一下LosFormater,再来细说.
我们再回来看打出来的结果B:t<1256029107;;l>D3i s-!
t,实际上通过查看LosFormatter反编译后的代码,大致上可以看出它序列化的方式是很简单的,就是判断要序列化对象的类型,如果不是直接序列化的类型,则把它的类型记录下来,然后在递归序列化它的属性,我们看B中的"t"就是表示Triplet这个类型,这个类型有三个属性,这三个属性包含在"<"和">"之间,用";"分割,而最后面的D3i s-!
t据我分析应该是一个防止ViewState被改变的Hash值,这个不是很确定,因为反编译的代码实在是很难看,我只是了解之后就没仔细看了。
我们刚刚分析出来Page中的ViewState反序列化之后是Triplet这个类型,实际上这个类在MSDN中就查得到,它就是一个包含了三个对象的对象,说简单点,它就是一个能放三个箱子的大箱子(好像还是说的比较糊涂,呵呵),它有三个属性:First、Second、Thrid :),分别代表三个对象。
对应到Page当中,First是Page.GetTypeHashCode()的返回值,这个方法是System.Web.UI.Page定义的一个保护的虚拟方法,返回一个整型,由Aspx文件生成的类来实现的,因为这个类是有Asp.Net负责在运行期生成源代码并编译,它会计算出一个大常量作为返回值,这个返回值在整个Web应用程序所有的Page中是唯一的。(提一句题外话,Asp.Net自动产生的源代码可以到 系统盘:/WINDOWS/Microsoft.NET/Framework/v1.0.3705/Temporary ASP.NET Files下面去找),这个唯一的Hash值是为了在ViewState中产生一个标记,使这个ViewState只适用与对应的页面。
Second则是通Control.SaveViewStateRecursive方法递归保存页面控件树的ViewState返回的对象,也就是真正的ViewState的数据。
Third中保存的是当前页面需要PostBack的控件名的列表。
分析了页面的ViewState的构成,我们再来看Control的ViewState的实现。ViewState是System.Web.UI.Control类实现的一个属性,这个属性的类型是System.Web.UI.StateBag,这个类就包含了ViewState数据结构的实现,实际上它的内部也就是个Hash表,通过Key值来保存和检索数据。
那么服务器控件是怎么实现保存状态的呢?
我们知道,所有的服务器控件都是从System.Web.UI.Control派生的,所以都拥有ViewState这个属性,在Control内部,定义了两个Protected的虚拟方法:
protected virtual object SaveViewState()
和
protected virtual void LoadViewState(object savedState)
这两个方法是给子控件派生用来保存和读取自己的ViewState的,比如我们有一个自己写的控件,往ViewState中保存了一个字符串,那么我们的方法大致像这样:
protected virtual object SaveViewState()
{
object[] states = new object[2];
states[0] = base.SaveViewState(); //记得保存父控件的ViewState
states[1] = "Hello,I'm timmy!"; //这里保存我们自己的
return states; //返回重新包装后的保存对象
}
获取的时候:
protected override void LoadViewState(object savedState) //这里的savedState就是我们Save的时候return 的object数组
{
object[] states = (object[])savedState;
base.LoadViewState( states[0] ); //把父类的数据给他自己去解析
string myData = (string)states[1]; //获取我们自己的数据
}
我们可以按照自己的方式来保存,不一定非要像上面这样用数组,实际上我们可以用任何支持序列化的对象都可以,父类并不关心子类如何保存,我们只要在Save和Load的时候使用同样的方式,并且把正确的数据传递给父类方法就可以了。
另外,还有一个问题就是我们使用的Control的ViewState是Key-Value这样的键值对,那它是怎么保存的呢?
实际上很简单,System.UI.Web下面有一个类叫Pair,呵呵,这个和Triplet差不多,只是它里面只有两个对象。StateBag保存的时候,First会存放所有Key值的数组,Second则存放所有Value的数组。
到现在,我们了解了ViewState是如何序列化并且保存到客户端,也了解了控件怎么保存自己的ViewState,那么这二者是怎么结合的呢?
也就是整个页面的控件树的ViewState是怎么保存和读取的呢?
在Control内部有两个internal的方法:
internal object SaveViewStateRecursive();
internal void LoadRecursive();
这两个方法由System.Web.UI.Page来调用,Page在Render结束后就会调用SavePageViewState方法,SavePageViewState方法会调用Control的SaveViewStateRecursive()方法,这个方法就是通过递归调用每一个Control.Controls的SaveViewStateRecursive方法来保存控件树中所有控件的ViewState。到这里,可能聪明的朋友要问了,既然SaveViewStateRecursive是递归调用保存的方法,那么我们上面写的SaveViewState()方法又有什么用呢?
我们知道,Control.Controls可能会有很多个,而且我们的SaveViewState()只保存了当前控件的数据,而没有记录控件树的结构,那么如果我们递归SaveViewState()方法来保存数据的话,那么控件树的结构就会丢失,那么Load的时候就没办法还原了,实际上在SaveViewStateRecursive方法中大致的代码是这样:
[1] 获取控件自己的ViewState(调用SaveViewState方法)
[2] 循环子控件
{
定义两个动态数组,一个保存控件的索引,一个保存递归调用子控件SaveViewStateRecursive方法返回的值
}
[3] 定义一个Triplet(呵呵,这个东西又出现了)
[4] First保存本控件的ViewState
[5] Second保存子控件的索引
[6] Third保存递归子控件SaveViewStateRecursive方法的返回值
[7] 返回Triplet
这样就保存了整个控件树的ViewState和控件树的结构
Load的方式与Save差不多,只是Load的时候会从savedState中获取子控件的索引来依次递归子控件的LoadRecursive()方法,这样才能保证正确的把保存的数据传给子控件。
到这里,ViewState的实现我们大致了解了一下,最后得出一些结论:
1、ViewState是存放在客户端,因此会减轻服务器的负担,是一种比较好的保存数据的方式。
2、因为ViewState本身的限制,只能保存可以序列化的对象,而且最好不要放太多东西,能省则省,以免在减慢传输的速度,以及加重服务器解析的负担。
3、我们通过很简单的方式就可以把ViewState里面的值获取出来,我们上面讨论了一些,虽然没有把解析的代码写出来,但是利用LosFormatter可以得到ViewState反序列化后的对象,那么要解析出来简直是易如反掌;所以ViewState在安全性上面还是比较差,建议不要
存放比较机密和敏感的信息,尽管ViewState可以加密,但是由于ViewState要保存在客户端,天生就有安全性的隐患。
4、实际从技术角度,ViewState没有任何新意,但是结合服务器控件的设计还是很巧妙的。
最后,以我个人的观点,我觉得ViewState的出现很大程度上减轻了程序员的负担,但是要看清的是ViewState的本质,合理的应用它。
匆忙写就难免有很多问题,还希望大家多提意见,不足之处请多指教!
相关文章:

【51CTO学院三周年】遇到
作为一名二流学校的大学生,因为学校的一门嵌入式Linux应用程序开发而喜欢上了嵌入式,但是学校却是只上了一学期的课,无奈只能自己找教程继续学习。在3个月前,无意中找到了朱有鹏老师的嵌入式学习基础视频,通过老师视频…

ASP.NET图象处理详解
作者:未知 请与本人联系在使用ASP的时候,我们时常要借助第三方控件来实现一些图象功能。而现在,ASP.NET的推出,我们已经没有必要再使用第三方控件来实现,因为ASP.NET 已经具有强大的功能来实现一些图象处理。现在&…

IT工作者,你们的爱情是这样的吗?
今天在博客里看到了这篇文章,看完这个视频我随笔写了点自己的感受和看法, 视频链接在下方: http://leidu.blog.51cto.com/3245712/622534 很感谢90 男孩提供,建议IT人员看一下,看完写一下你们的感受吧!…
平头哥玄铁处理器Linux新版本,5大亮点速览
来源 | 芯片开放社区为了便于 CPU 评估,系统集成,快速上手玄铁处理器 Linux 操作系统,平头哥更新了玄铁处理器 linux 版本,结合 gitlab 开源 CI/CD 系统,对已发布到开源社区的玄铁架构 CPU 相关的生态软件形成持续保障…

费用保险单,如何失焦时自动补零
费用,如何失焦时自动补零转载于:https://www.cnblogs.com/maojiayan/p/5606247.html
腾讯AI种番茄双丰收:参赛AI全胜专家,辽宁试点净利增千元
6月9日,腾讯宣布了两项AI农业领域进展。在研究侧,腾讯 AI Lab 与荷兰瓦赫宁根大学(下称WUR)联办的“第二届国际智慧温室种植挑战赛”(下称比赛)落幕。在全球疫情肆虐之时,复赛的五支队伍挑战用 …

在ASP.NET中值得注意的两个地方
在ASP.NET中ASPX页面的Page_Load事件有两个让人奇怪的地方,你应该记住它们: a.有时Page_Load事件在你的ASP.NET页面里会发生多次。这种情况发生的一个可能的原因是你把ASPX页面的AutoEvenWireup值设置成了True。如果是这样,那么在“Sub Page…

yii 级联删除
alter table 外键表 add constraint 级联删除名 foreign key (外键字段) references 主表名(主表字段)ON delete CASCADE go 转载于:https://blog.51cto.com/dasangshu/624605

iOS--优秀博客记录
感谢唐巧整理,我又加了一些备注。原地址:https://github.com/tangqiaoboy/iOSBlogCN 博客地址RSS地址psOneVs Denhttp://onevcat.com/atom.xml 一只魔法师的工坊http://blog.ibireme.com/feed/图片处理、YYKit破船之家http://beyondvincent.com/atom.xml…

linux环境下和网络服务相关的配置文件含义及如何配置
要建立一个安全Linux服务器就首先要了解Linux环境下和网络服务相关的配置文件的含义及如何进行安全的配置。那天查看服务器的eth0地址,后来想了一些问题,到家里就翻了翻以前的文档,无意中看到了这个,现在发布出来,希望…

ASP.NET保持用户状态的九种选择
2003-06-10 ■陶刚编译 ■yesky摘要:ASP.NET为保持用户请求之间的数据提供了多种不同的途径。你可以使用Application对象、cookie、hidden fields、Sessions或Cache对象,以及它们的大量的方法。决定什么时候使用它们有时很困难。本文将介绍了上述的技术&…
Python 图像处理 | 图像平滑之均值滤波、方框滤波、高斯滤波及中值滤波
作者 | 杨秀璋,责编 | 夕颜 题图 | 视觉中国出品 | CSDN博客本篇文章主要讲解Python调用OpenCV实现图像平滑,包括四个算法:均值滤波、方框滤波、高斯滤波和中值滤波。全文均是基础知识,希望对您有所帮助。知识点如下:…

Laravel Lumen之Eloquent ORM使用速查-基础部分
使用Eloquent [eləkwənt] 时,数据库查询构造器的方法对模型类也是也用的,使用上只是省略了DB::table(表名)部分。 在模型中使用protected成员变量$table指定绑定的表名。<?php namespace App;use Illuminate\Database\Eloquent\Model;class Flight…

数据库连接字在Web.config里的用法
作者:未知 请速与本人联系在asp.net中的WEB程序的设置中我们必须用到Web.config来存储数据库连接字.事实上这是个很好的做法,因为可以省去我们很多的麻烦还可以帮助我们避免不必要的错位,是的很多情况下我就是这样做.它通过XML来记录这些信息.具体的是在....这个标…
重构ncnn,腾讯优图开源新一代移动端推理框架TNN
来源 | 腾讯优图从学界到工业界,“开源”已经成为AI领域的一个关键词。一方面,它以“授人以渔”的方式为AI构建了一个开放共进的生态环境,帮助行业加速AI应用落地;另一方面,在解决行业实际问题时持续更新和迭代&#x…

java读取文件
1 java8读取文本文件2 3 4 public static void java8ReadFileLines(String fileName) throws IOException {5 List lineList Files.readAllLines(Paths.get(fileName), StandardCharsets.UTF_8);6 7 for(String line:lineList){8 Sys…

常见 Datagrid 错误
Marcie Robillard DatagridGirl.comDatagrid 控件是 Microsoft ASP.NET 中功能最强、用途最广的 Web 控件之一,这一点已经得到了 ASP.NET 权威人士的认同。虽然 Datagrid 控件易于使用,但同样易于给使用者带来麻烦。以下是许多人所犯的一些错误ÿ…
干货!3 个重要因素,带你看透 AI 技术架构方案的可行性!
作者 | 房磊责编 | Carol出品 | AI 科技大本营(ID:rgznai100)人工智能这几年发展的如火如荼,不仅在计算机视觉和自然语言处理领域发生了翻天覆地的变革,在其他领域也掀起了技术革新的浪潮。无论是在新业务上的尝试&…

mysql从另一张获取数据的方法
方法一 CREATE TABLE tmp AS SELECT a.id FROM t_user t JOIN temp a ON t.email a.email; 方法二 INSERT INTO t_user (id,username,PASSWORD,email,user_type,STATUS) SELECT id,REPLACE(email,,_),PASSWORD,email,0,0 FROM temp; 这两个sql都是从另外一张中获取的数据插入…

手动创建Spring项目 Spring framework
之前学习框架一直是看的视频教程,并且在都配套有项目源码,跟着视频敲代码总是很简单,现在想深入了解,自己从官网下载文件手动搭建,就遇到了很多问题记载如下。 首先熟悉一下spring的官方网站:http://spring…

使用 ASP+ DataGrid 控件来创建主视图/详细资料视图
Nikhil Kothari Microsoft Corporation 2000年8月简介 Microsoft Visual Studio.NET 的下一发行版包括 DataGrid Web 控件 (作为服务器控件的 Active Server Page (ASP) 套件的一部分)。 该控件提供用以根据数据源的内容来表示 HTML 的功能。 DataGrid 控件可以用于若干个只…

如何将广告始终定位到网页右下角
body{margin:0;border:0;height:100%;overflow-y:auto;}#test{display:block; bottom:3px; right:3px; width:130px; position:fixed;}/* 以下是写给IE6的 */* html #test{position:absolute;right:18px} * html{overflow-x:auto; overflow-y:hidden;}转载于:https://www.cnblo…
如何用 Python 将 Excel 表格转成可视化图形?| 原力计划
作者 | Waao666责编 | 王晓曼出品 | CSDN 博客前言大家知道,考研很大一部分也是考信息收集能力。每年往往有很多人就是在这上面栽跟头了,不能正确分析各大院校往年的录取信息,进而没能选择合适的报考院校。至于很多院校的录取信息是以 PDF 形…

Mac OS X 下mysql配置备忘
从windows过渡到os x确实需要适应,对于开发人员来讲更是这样。从官网下载目前最新版本的mysql 5.7.13,下载地址:http://dev.mysql.com/downloads/mysql/刚开始非常顺利的安装完mysql,这时候我还没有意识到密码的问题,直…

为 ASP.NET Datagrid 创建自定义列
Marcie Robillard DatagridGirl.com 2003 年 9 月 简介 不得不承认,为 Microsoft ASP.NET 编写 Datagrid 代码包括大量的重复工作。尽管我深受该控件的困扰,但我还是不断寻找简化这类任务的捷径。谁都不愿意做重复的工作,对不对࿱…
不怕面试被问了!二叉树算法大盘点
作者 | BoCong-Deng题图 | 视觉中国出品 | CSDN博客树结构对于程序员来说应该不陌生,特别是二叉树,基本只要接触算法这一类的都一定会碰到的,所以我打算通过一篇文章,对二叉树结构的相关算法进行总结汇总,思路和代码实…

Field types
2019独角兽企业重金招聘Python工程师标准>>> Field types The generated Form class will have a form field for every model field. Each model field has a corresponding default form field. For example, a CharField on a model is represented as a CharFie…

【51CTO学院三周年】我的职业生涯有贵人相助--小强老师
个人认为功能测试做到一定年限之后,自然会遇到职业生涯中最大瓶颈——转型。对此,我的经历是这样的。话说那还是两年前,在搜索某问题的时候发现了51CTO,从中看到了很多大牛的博文和视频课程,顿时感觉自己找到宝了&…

在.NET中实现彩色光标,动画光标和自定义光标
作者:孟宪会 微软MVPTest.cs using System;using System.Drawing;using System.Windows.Forms;using System.Runtime.InteropServices;using System.Reflection; namespace ColorCursor{ public class Form1 : System.Windows.Forms.Form { [DllImport("us…

magento模板区块--首页content区块
首页替换 自定义首页content内容 在cms-->>page 新建首页 在content 里加入 -------------------------- <div class"col-left side-col"> <p class"home-callout"><a href"{{store direct_url"apparel/shoes/womens/anash…