破宝

我是一块破破烂烂的宝贝石头。
随笔 - 85, 评论 - 1279, 引用 - 54

导航

工具

关于

自选精华版 RECOMMENDATIONS
留言板 GUESTBOOK

本人 blog 文章、图片及其他资源等,除另有声明外,均遵循以下原则向全球(当然包括朝鲜、古巴、利比亚等国)共享:

1。欢迎转载、复制、传播、引用,但转载、复制(包括但不仅限于作为参考资料复制到本地)、传播、引用同时必须在显著位置注明作者(破宝/percyboy)和文章原始 URL 地址等信息。但商业转载、复制、传播(尤指用于图书、光盘等媒体的部分或全部),须事先征得本人的许可。

2。文章以“现状”提供,不为由于使用本站资源而造成的任何损失而负责,仅提供力所能及的咨询和参考意见。

3。关于修改:允许您将本 blog 中的资源作为参考资料复制时的一定修改,但仍须保留作者和出处信息;其他情况下的修改(包括修改后再发布),须和本人确认许可。
 

标签

每月存档

广告



访客

 

Web 页本是无状态而断续的

Web 模型,B/S 是 C/S 的一个特例,但它仍然延续了 C/S 的“请求”-“响应”机制:从接到请求,分析请求并根据请求、在服务器上索取响应数据库及其他资源,加工处理形成一份 HTML 页面(这里可能会包含客户端脚本以达到特定效果),然后向客户端浏览器发回“响应”。Web 就这样一个来回(loop),一个来回的运行着。

这样来说,Web 显然是断续的。那“无状态”怎么讲?以 Windows 程序为例,比如文本框,它的 Text 属性值如果发生改变,你可以知道改变前的值和改变后的值,这就是状态的一个作用。

传统 Web 处理引擎(CGI, ASP, PHP, JSP 等)的编程,就基本上沿用着这套线性的模型。

ASP.NET Web Form 的“连续”和“有状态”假象

从根本上说,ASP.NET 并没有改变 Web 页的本质:每次请求 ASP.NET 页时,服务器就会加载一个 ASP.NET 页,并在请求完成时卸载该页。页及其包含的服务器控件负责执行请求并将 HTML 呈现给客户端。

ASP.NET 的设计者们,从实际访问者的角度重新考虑了这一过程:访问者打开一个页面,点击一个按钮,看到新的画面……这一切似乎都是连续的。

这种连续性假象是由 ASP.NET 页框架、页及其控件实现的。回发后,控件的行为必须看起来是从上次 Web 请求结束的地方开始的。另一方面,对于 Web Form 中的 TextBox,ASP.NET 也让它们具有了状态,可以知道上一个 loop 和这一个 loop 之间的 TextBox 值的变化;如果变化,可能会触发 TextBox 的 TextChanged 事件。这同样是 ASP.NET 特意实现的一个假象。

ASP.NET 服务器控件的生命周期一般如下:

1. 初始化  - Init 事件 (OnInit 方法)

2. 加载视图状态 - LoadViewState 方法

3. 处理回发数据 - LoadPostData 方法
    对实现 IPostBackDataHandler 接口的控件,即可以自动加载回发数据的控件,如 TextBox, DropDownList 等。

4. 加载 - Load 事件 (OnLoad 方法)

5. 发送回发更改通知 - RaisePostDataChangedEvent 方法
    对实现 IPostBackDataHandler 接口的控件,即可以自动加载回发数据的控件。
    在第 3 步中加载回发数据,如果回发前后数据发生更改,则在这一步触发相应的服务端事件。

6. 处理回发事件 - RaisePostBackEvent 方法
    对实现 IPostBackEventHandler 接口的控件,即能引起回发的控件,如 Button, LinkButton, Calendar 等

7. 预呈现 - PreRender 事件 (OnPreRender 方法)

8. 保存视图状态 - SaveViewState 方法

9. 呈现 - Render 方法

10. 处置 - Dispose 方法

11. 卸载 - UnLoad 事件 (OnUnLoad 方法)

Web Form 的基类 System.Web.UI.Page 从 System.Web.UI.Control 继承,它也是一种特殊的 Control。

ASP.NET 是怎样实现状态的?

ASP.NET 使用了 ViewState 视图状态,如果你查看 Web Form 产生的 HTML 代码,可以看到一个名为 __ViewState 的隐藏字段,ASP.NET 将状态信息以 Hash 的方式存储在这里。通过它,可以在下一次回发时知道回发前各控件的状态。

比如:一个 TextBox,回发前 Text 属性有值“hello”,访问者填写了新的值“world”,当这个页面回发到服务器端,服务端代码就可以得知 TextBox 的 Text 属性值发生了改变,TextChanged 事件就被触发了。从生命周期来看,LoadViewState 这一步加载了 TextBox 的原状态,LoadPostData 这一步从 Request.Form 集合中取得了 TextBox 的当前值,过了 Load,在 RaisePostDataChangedEvent 这一步触发 TextBox 的 TextChanged 事件,SaveViewState 将当前值存入 ViewState 作为下一次回发的原状态。

ASP.NET 是怎样实现连续性假象的?

对开发者而言,以往对于一个提交按钮的点击回发,或者说 HTML form 提交的处理往往是在另一个页面中处理,将 form 的 target 指向该页面。(当然在一个页面中也是可以完成的,但大部分人习惯于两个页面)

在 ASP.NET 中,这一过程被处理成和 Windows 程序类似的过程,Button 的点击、form 被提交这个事件被 ASP.NET “包装”成一个服务器事件,也就是 Button 的 Click 事件。从生命周期来看,Button 控件被加载的流程如下:LoadPostData 这一步可以从 Request.Form 集合中找到 Button 的 name 值(只有被点击的 Button 会在 Request.Form 集合中生成一个 name-value 对);过了 Load,在 RaisePostBackEvent 这一步,将触发 Button 的 Click 事件。

我们再分析特别的情况:Button 在 HTML 中是可以引起 form 的提交的,也就是可以引起页面回发;但其他的,如 LinkButton (对应于 HTML 的 A 元素),DropDownList (对应于 HTML 的 SELECT)等,则并不会自动引起回发。这种情况下,ASP.NET 使用了又一个技巧来保证这一假象继续成立:打开含有 LinkButton 的一个 ASP.NET 生成的 HTML 页面代码,可以找到两个隐藏字段,一个叫 __EVENTTARGET,一个叫 __EVENTARGUMENT,再往下找到一段脚本:

 function __doPostBack(eventTarget, eventArgument) {
  var theform;
  if (window.navigator.appName.toLowerCase().indexOf("netscape") > -1) {
   theform = document.forms["Form1"];
  }
  else {
   theform = document.Form1;
  }
  theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
  theform.__EVENTARGUMENT.value = eventArgument;
  theform.submit();
 }

再看看 LinkButton 生成的代码:

<A id=LinkButton1 href="javascript:__doPostBack('LinkButton1','')">LinkButton</A>

如果你做过网页中的客户端脚本的话,应该知道,当点击这个“LinkButton”时,实际上是通过客户端脚本将它的名字和若干参数(比如 Calendar 需要传递一些参数,LinkButton 没有传参数的必要)设置到两个隐藏字段中,并在脚本中提交了表单。

继续看服务段的流程:LoadPostData 会看到这两个隐藏字段中的值,但并不马上解析;依然是过了 Load,在 RaisePostBackEvent 这一步解析这两个字段中的值,触发相应控件的事件。

我们最后分析一下 CheckBox 或者 DropDownList 之类的 AutoPostBack 属性:如果 AutoPostBack 为 true,则在向客户端输出时,加入上面的 __doPostBack 式的回发;如为 false,则不加入这样的立即回发脚本,而是等待有其他可以引起回发的控件(比如 Button, LinkButton 等)回发后,在 RaisePostDataChanged 中触发 CheckBox 的 CheckedChanged 事件,DropDownList 的 SelectedIndexChanged 事件;在 RaisePostBackEvent 时继续分析其他引起回发的事件。

 

微软把复杂的 Web 模型简化成一个传统 Windows 程序员易于接受的模型,大大降低了 Web 开发的门槛。但尽管如此,微软无法改变 Web 的“无状态”、“断续”的实质,所以不要将所有 Windows 程序开发的经验都一古脑运用到 Web 开发当中。了解了其内在机理,有助于 Windows 程序员避免这些“武断”的错误。

相关文章

Loading...

打印 | 张贴于 2004-08-22 20:52:00 | Tag:暂无标签

留言反馈

#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
我想问一下,为什么自定义控件在实现IPostBackEventHandler,IPostBackDataHandler这两个接口后,LoadPostData可以到回发信息而RaisePostBackEvent根本就不会被系统执行??
2005-12-28 12:54:00 | [匿名用户:甲]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
因为我发现如果我有两个服务端button控件,其中A在客户端调用了B的click方法,在B的服务端click事件中我又注册了一个脚本,而由于A会自动postback,所以这段注册的脚本被冲掉了。这个你的能举个例子是怎么用的嘛?
2005-12-18 20:06:00 | [匿名用户:heilong05@163.com]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
我想问一下有没有什么办法可以禁止一个服务端button控件不自动postback?
因为我发现如果我有两个服务端button控件,其中A在客户端调用了B的click方法,在B的服务端click事件中我又注册了一个脚本,而由于A会自动postback,所以这段注册的脚本被冲掉了。

我知道可以的办法是将A换成html控件并设为runat server,但总觉得这样麻烦。
因为我没有注册A的服务端click事件,所以不知道有没有办法让它在被click时不要postback?


谢谢!
2005-07-21 20:17:00 | [匿名用户:davidjgu]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
看完你的文章很有感触,感觉思想突然明白了许多东西,好文章!谢谢
2005-07-10 22:02:00 | [匿名用户:nancy]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
如果TextBox控件是通过 Request.Form 集合获得属性,那对于列表控件呢,它的加载过程是怎么样的呢?

我想肯定是通过已编码的特殊字符串把不同的意义通过form传入服务器端,textbox也不知是传数据这么简单,比如设置此textbox,无效或者隐藏,那么无效或者隐藏这个属性也会被保存起来。
2005-06-29 01:17:00 | [匿名用户:jamessen]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
不错
2005-02-10 11:54:00 | [匿名用户:sena]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
不错的文章,谢谢啦!
2004-12-28 12:53:00 | [匿名用户:菜鸟]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
good
2004-11-26 20:03:00 | [匿名用户:usherlight]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
asdfa
2004-11-15 19:05:00 | [匿名用户:asdf]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
如果TextBox控件是通过 Request.Form 集合获得属性,那对于列表控件呢,它的加载过程是怎么样的呢?
2004-11-03 10:13:00 | [匿名用户:who]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
I love this talk!
2004-10-14 08:40:00 | [匿名用户:Chap]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
对于服务器控件的执行过程有了更清晰的了解
2004-08-31 10:59:00 | [匿名用户:Megres]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
实际上,ViewState是实现client-side statefulness的关键之一,因为如果server-side是stateful的,会大大降低整体架构的可伸缩性——然而传统的cookie-based、query-based client-side stateful机制都有很多限制,而利用form post则引入过多开发问题,所以ASP.NET的核心技术之一就是利用ViewState来透明的封装状态持久化,以在不影响后端的可伸缩性和增加开发难度的前提下提供更好的应用开发模型——虽然这不可避免的增加了网络通信量。

Hint: 有没有可能利用IE的client-side persistence机制来取代ViewState呢?好处是减少网络上的传输,对于低带宽应用有利;不好的因素是增加了浏览器的依赖性,只适合用于可控制的部署环境,而这类环境又大多拥有良好的带宽……呵呵,just a brainstorm.
2004-08-23 22:07:00 | [匿名用户:JGTM'2004 [MVP]]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
我明白你的意思,从更底层来看的话,实际上http协议是一个无状态的协议,导致了不得不在Post回去的数据中增加更多的状态信息,也就是经常性的stateless to stateful的转变。
2004-08-23 11:10:00 | [匿名用户:小峰]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
Very good!
2004-08-23 10:51:00 | [匿名用户:开心就好]
#ASP.NET Web Form 的“连续”和“有状态”假象 编辑
Ping Back来自:blog.csdn.net
2004-08-23 10:03:00 | [匿名用户:kwklover]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
这里说的状态特指“视图状态”,即控件在每次回发前后的状态,比如文中说的 TextBox 在回发前后 Text 属性值的原值和新值。

“视图状态”是在 ASP.NET 服务器控件内部已经实现了的,我们一般并不直接访问“视图状态”。

小峰说的 Session, Application, Cookie 这些是在外部代码中存储一些信息,它们所维护的“状态”是程序特定的需求:比如存储用户名到 Session 中;它们一般并不是存储某个控件状态的原始值或者当前值,和文中的意思不同。Cache 从设计初衷而言,并不是为了存储状态的,虽然你可以那么用。

当然有人会说,也可以在 Web Form 代码中访问到 ViewState,并使用它存储一些程序特定的需求值,这个也是可能的,也是和文中说的不是一回事。

文中只是在描述 ASP.NET 内部已经实现的 ViewState 作用。
2004-08-23 09:26:00 | [匿名用户:破宝]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
Perfect Article !
2004-08-23 09:25:00 | [匿名用户:kaneboy]
#re: ASP.NET Web Form 的“连续”和“有状态”假象 编辑
实际上,ViewState只是维护状态的一种方式,还有Session,Cookies,Application,Cache等多种方式来维护状态
2004-08-23 00:50:00 | [匿名用户:小峰]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode MVC Blogger System