ASP.NET页面解析(3)
事件模型
当某个页面被请求时,它的类及其所包含的服务器控件会负责处理请求,呈现HTML输出,并随后发回客户端。由于HTTP协议的固有特性,客户端与服务器间的通信是无状态且非连接的。而实际的应用程序需要状态,以便维护对同一页面的后续调用。使用ASP或其他服务器端开发平台(如Java Server Page和LAMP一类的基于Linux的系统),开发者必须负责状态的存储。而ASP.NET提供了一种内建架构,能够以透明的方式对页面的状态进行存储和恢复。尽管基于无状态的协议,但以这种方式,从客户端体验到的是连续的执行过程。然而,那只是一种表象。
视图状态简介
连续性所导致的这种表象,一方面与页面的设计和工作方式有关,另一方面是ASP.NET页面视图状态造成的。与此同时,服务器端控件也发挥着重要作用。简而言之,在页面将其内容呈现为HTML之前,页面要将自身及其所包含的控件的状态信息存储在持久性介质(一般为隐含字段)中。当该页面回发后,其状态会从隐含字段中被反序列化,用于对声明在页面布局中的服务器控件实例进行初始化。 每个页面实例有其特有的视图状态,因为该信息嵌入在HTML中。这样做的好处是,控件会以上一次创建的视图状态(即该页最后一次被呈现发送到客户端时的状态)的值进行初始化。此外,页面周期中还会有一个阶段,将已存储的状态与由客户端做出的更新合并。在回发后,页面执行时,它会发现一个有状态的且更新过的上下文,就像工作在连续的点对点连接上一样。 这里做了两个假设。第一个假设是,页面总是投递给自身,并携带着状态信息。第二个假设是,服务器端控件必须带有runat=server属性,以便在页面回发后具有“生命力”。
单窗体模型
不可否认,对于具有ASP或JSP经验的程序员来说,开始可能不太适应ASP.NET的单窗体模型。这些程序员在论坛和新闻组经常会问这样的问题:“窗体的Action属性在哪里?”以及“为什么我提交窗体时,不能重定向到一个特定页面?” ASP.NET页面只支持一个服务器端<form>(窗体)标签。所有要与服务器交互的控件,必须全部置于在该窗体中。窗体和控件都必须带有runat属性,否则会被视为纯文本,并被逐字输出。在服务器端,窗体是HtmlForm类的实例。HtmlForm类没有暴露任何相当于HTML <form>标签的Action的属性。其原因在于,ASP.NET页面总是投递给自己。除Action属性外,窗体其他常用属性(如Method和Target)还是完全支持的。 不包含服务器端窗体的,以及使用HTML窗体(不带runat属性的<form>标签)的页面,也是有效的ASP.NET页面。在ASP.NET页面中,HTML和服务器窗体可以同时存在。然而只能有一个<form>标签的runat属性设置为server。HTML窗体会像一般情况一样,使我们能够向程序中的任何页面投递。但这样的做问题在于,状态信息不会被自动存储。换言之,仅当窗体使用一个服务器<form>元素时,ASP.NET Web窗体模型才会工作.
异步页面
ASP.NET页面会被HTTP处理程序作为Page类的实例处理。每个请求会占用ASP.NET线程池中的一个线程,在请求完毕后该线程才会被释放。倘若被请求的页面频繁地启动外部的、高耗时的任务呢?问题在于,ASP.NET进程虽然闲置,但池中没有空闲的线程来处理新入的其他页面的请求。这多应归因于HTTP处理程序(包括页面类)的同步工作方式。为减轻这个问题,自1.0版开始,ASP.NET支持了异步处理程序(通过IHTTPAsyncHandler接口)。而从ASP.NET 2.0版开始,由于有了框架的支持,创建异步页面变得更加容易了。 异步ASP.NET页面的构建涉及两个方面:@Page指令的一个新属性以及注册的若干异步执行的任务。异步任务可以通过两种途径注册。可以为PreRenderComplete事件定义异步处理程序Begin/End对儿,也可以创建代表异步任务的PageAsyncTask对象。这在PreRender事件被引发之前进行就可以,但一般是在Page_Load事件中。