ASP.NET页面生命周期描述 (已整理)

对由 Microsoft® Internet 信息服务 (IIS) 处理的 Microsoft® ASP.NET 页面的每个请求都会被移交到 ASP.NET HTTP 管道。HTTP 管道由一系列托管对象组成,这些托管对象按顺序处理请求,并将 URL 转换为纯 HTML 文本。HTTP 管道的入口是 HttpRuntime 类。ASP.NET 结构为辅助进程中的每个 AppDomain 创建一个此类的实例。(请注意,辅助进程为每个当前正在运行的 ASP.NET 应用程序维护一个特定的 AppDomain。)

HttpRuntime 类从内部池中获取 HttpApplication 对象,并安排此对象来处理请求。 HTTP 应用程序管理器完成的主要任务就是找到将真正处理请求的类。当请求 .aspx 资源时,处理程序就是页面处理程序,即从 Page 继承的类的实例。资源类型和处理程序类型之间的关联关系存储在应用程序的配置文件中。更确切地说,默认的映射集是在 machine.config 文件的 <httpHandlers> 部分定义的。但是,应用程序可以在本地的 web.config 文件中自定义自己的 HTTP 处理程序列表。以下这一行代码就是用来为 .aspx 资源定义 HTTP 处理程序的。
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
扩展名可以与处理程序类相关联,并且更多是与处理程序工厂类相关联。在所有情况下,负责处理请求的 HttpApplication 对象都会获得一个实现 IHttpHandler 接口的对象。如果根据 HTTP 处理程序来解析关联的资源 / 类,则返回的类将直接实现接口。如果资源被绑定到处理程序工厂,则还需要额外的步骤。处理程序工厂类实现 IHttpHandlerFactory 接口,此接口的 GetHandler 方法将返回一个基于 IHttpHandler 的对象。
HTTP 运行时是如何结束这个循环并处理页面请求的? ProcessRequest 方法在 IHttpHandler 接口中非常重要。通过对代表被请求页面的对象调用此方法, ASP.NET 结构会启动将生成浏览器输出的进程。
真正的 Page
特定页面的 HTTP 处理程序类型取决于 URL 。首次调用 URL 时,将构建一个新的类,这个类被动态编译为一个程序集。检查 .aspx 资源的分析进程的结果是类的源代码。该类被定义为命名空间 ASP 的组成部分,并且被赋予了一个模拟原始 URL 的名称。例如,如果 URL 的终点是 page.aspx ,则类的名称就是 ASP.Page_aspx 。不过,类的名称可以通过编程方式来控制,方法是在 @Page 指令中设置 ClassName 属性。
HTTP 处理程序的基类是 Page 。这个类定义了由所有页面处理程序共享的方法和属性的最小集合。 Page 类实现 IHttpHandler 接口。
在很多情况下,实际处理程序的基类并不是 Page ,而是其他的类。例如,如果使用了代码分离,就会出现这种情况。代码分离是一项开发技术,它可以将页面所需的代码隔离到单独的 C# Microsoft Visual Basic® .NET 类中。页面的代码是一组事件处理程序和辅助方法,这些处理程序和方法真正决定了页面的行为。可以使用 <script runat=server> 标记对此代码进行内联定义,或者将其放置在外部类(代码分离类)中。代码分离类是从 Page 继承并使用额外的方法的类,被指定用作 HTTP 处理程序的基类。
还有一种情况, HTTP 处理程序也不是基于 Page 的,即在应用程序配置文件的 <pages> 部分中,包含了 PageBaseType 属性的重新定义。
<pages PageBaseType="Classes.MyPage, mypage" />
PageBaseType 属性指明包含页面处理程序的基类的类型和程序集。从 Page 导出的这个类可以自动赋予处理程序扩展的自定义方法和属性集。
页面的生命周期
完全识别 HTTP 页面处理程序类后, ASP.NET 运行时将调用处理程序的 ProcessRequest 方法来处理请求。通常情况下,无需更改此方法的实现,因为它是由 Page 类提供的。
此实现将从调用为页面构建控件树的 FrameworkInitialize 方法开始。 FrameworkInitialize 方法是 TemplateControl 类( Page 本身从此类导出)的一个受保护的虚拟成员。所有为 .aspx 资源动态生成的处理程序都将覆盖 FrameworkInitialize 。在此方法中,构建了页面的整个控件树。
接下来, ProcessRequest 使页面经历了各个阶段:初始化、加载视图状态信息和回发数据、加载页面的用户代码以及执行回发服务器端事件。之后,页面进入显示模式:收集更新的视图状态,生成 HTML 代码并随后将代码发送到输出控制台。最后,卸载页面,并认为请求处理完毕。
在各个不同阶段里,页面处理了与 web 控件相关、程序员代码能够干预并解决一定问题的事件。其间一些事件是专门为那些内嵌控件和不能在 .aspx 代码级别处理的控件而设计的。
一个页面要解决这样的事件,它能明确的注册成为合适的句柄。但是,为了和原有的 Visual Basic 编程模式有后向兼容性, ASP.NET 也支持了隐含事件的形式。在默认情况下,页面会寻找和事件相关的方法名;如果找到和事件相匹配的方法,这个方法就被认为是这种事件的处理程序。 ASP.NET 提供了六种专门的方法名,他们是 Page_Init , Page_Load , Page_DataBind , Page_PreRender Page_Unload 。这些方法这些方法在 Page 类中已经被定义过,他们是相应事件的处理程序。 HTTP 运行时将自动的将这些方法绑定到相关的页面事件,而不需要程序员去编写把事件和方法联系起来的代码。举个例子来说,在下面的代码中, Page_Load 方法和页面的加载事件相关联:
this.Load + = new EventHandler(this.Page_Load);
这种自动识别是被 @Page 预指令的 AutoEventWireup 属性控制的。如果这个属性被置 false ,应用程序必须显式声明和事件相关的方法。不自动关联页面事件代码的页面执行起来会快一些,是因为他们不需要在匹配上做过多的工作。在 Visual Studio.NET 工程里可以把这个属性关闭掉。但是,默认设置是 true ,这意味着 Page_Load 方法被自动识别并被关联到相关的事件。
ASP.NET页面生命周期描述 (已整理)
实例化 (Instantiate) :控件在页面或其它控件通过调用其构造函数所实例化时。只有当一个控件在加入控件树中后,该步骤后的所有阶段才会发生。
初始化 (Initialize) :在此阶段中,控件树中的页面与全部控件通过默认的方式调用 OnInit 方法来触发 Init 事件,并完成初始化工作。在整个生命周期之前,页面首先建立初始的控件树;在初始阶段之前,为控件赋值。可以实现 Page_Init 方法来对页面的初始化进行影响,也可以重载控件的 OnInit 方法来为控件提供初始化逻辑。在这个阶段,控件可以访问其包含的子控件,但却不能访问其父控件或更高层次的控件。
开始跟踪视图状态 (Begin Tracking View State) :这个阶段发生在初始化阶段的末属阶段。在该阶段中,页面自动调用控件的 TrackViewState 方法,从而启用视图跟踪。当控件提供复杂的属性时,可以重载该方法。
加载视图状态(仅用于回传过程) (Load View state) :这个过程发生在回传时,而不是初始请求过程中。在此阶段中,控件会将其状态恢复到上一次请求处理完成之后的状态,同时,页面框架自动恢复 ViewState 字典。
加载回传数据 ( 仅用于回传过程,为可选项 )(Load Postback Data) :只在控件通过实现 IPostBackDataHandler 接口参与了回传数据处理时,这个阶段才发生在回传中。在这个阶段中,控件通过从已发送的表单数据中利用 IPostBackDataHandler 接口的 LoadPostData 方法更新其状态。
加载 (Load) :直至该阶段开始,页面中所有控件都已被初始化,回恢复到它们先前周期的最后状态。 ]
引发修改事件 ( 仅用于回传过程,可选项 ) :只有在控件通过实现 IPostBackDataHandler 接口参与回传数据处理时,此阶段才会发和在回传中。在些阶段中,控件通过引发事件作为一种信号,即控件状态由于回传而修改。为了参与此阶段,控件必须实现 IPostBackDataHandler 接口的 RaisePostDataChangedEvent 方法。
引发回传事件 ( 仅用于回传过程,可选项 ) :只有在控件通过实现 IPostBackEventHandler 接口参与回传事件处理时,此阶段才会发生在回传中。在此阶段,可以通过 [ 实现 IPostBackEventHandler 接口的 RaisePostBackEvent 方法来实现逻辑,以便把客房事件映射到服务器端事件。
预生成 (PreRender) :在此阶段中,应该通过重载 OnPreRender 方法,执行在生成控件之前所需要的任何工作。
保存视图状态 (Save View State) :在此阶段,页面框架自动保存 ViewState 字典,如果控件需要自定义状态管理,必须通过重载 SaveViewState 方法来实现自定义状态恢复。这种方法只被 EEnableViewState 属性为真的控件所调用。
生成( Render)
卸载 (Unload)
释放 (Dispose)

阶段
页面事件
可覆盖的方法
页面初始化
Init
加载视图状态
LoadViewState
处理回发数据
任意实现 IPostBackDataHandler 接口的控件中的 LoadPostData 方法
加载页面
Load
回发更改通知
任意实现 IPostBackDataHandler 接口的控件中的 RaisePostDataChangedEvent 方法
处理回发事件 ( 客户端事件 )
由控件定义的任意回发事件
任意实现 IPostBackDataHandler 接口的控件中的 RaisePostBackEvent 方法
页面显示前阶段
PreRender
保存视图状态
SaveViewState(SaveControlState2.0 新增 , 类似 ViewState 作用,但它们区别在于 ControlState 用于保存更加重要的控件状态信息,以保证在禁用 ViewState 的情况下还可以对控件状态进行读写操作。 )
显示页面
Render
卸载页面
Unload
(1) 对象初始化 Init 事件:页面初始化的标志是 Init 事件。页面中的控件(包括页面本身)都是在它们最初的 Form 中被首次初始化的。这个事件在控件树被构建出来后执行 , 在成功创建页面的控件树后,对应用程序激发这个事件。当 Init 事件发生时,在 .aspx 源文件中静态声明的所有控件都以实例化并取其默认值。在 Init 事件中可以初始化任何的在页面生命周期里需要的设置。例如:在这个阶段,控件可以加载外部的摸版文件或者是为事件建立处理句柄 , 应该注意到,这是还没有视图状态信息可供使用。虽然可以重载 OnInit 方法。
一旦你在你的构造函数中声名了所有的对象,你就可以通过继承类、方法、事件或是属性访问它们。然而,如果你的一些对象是在 Aspx 文件中指定的一些控件,那么这些控件就没有属性可言了。同时,通过代码访问它们会产生一些意外的错误,因为这些控件实例是没有一个确定的创建顺序的(如果它们是被一起创建的)。
ASP.NET页面生命周期描述 (已整理)
(2) 加载视图:在初始化之后,页面框架立即加载该页面的视图状态( ViewState )。所谓视图状态就是一些名称 / 值对的集合,例如可以保存 TextBox 控件的 ID Text 属性值。它一般被用于在一个往返行程中存留信息到服务器,即参与 HTTP 请求与响应。
这种方式比起 Asp3.0 的维持、判断页面状态的方式有了很大的进步啊。还有,你可以重载 LoadViewState 事件函数来对相应的控件进行值设定。下图(图 2 )是一个例子:
ASP.NET页面生命周期描述 (已整理)
页面视图状态被存储在 <input type=”hidden”> 字段中,做为 _VIEWSTAE 的值进行记录。该视图状态通过 ASP.NE 自动维护。通过重写 LoadViewState 方法组件,开发人员可控制如何还原视图状态以及如何将其内容影射到内部状态。 LoadViewState 方法就是从 ViewState 中获取上一次的状态,并按照页面的控件树的结构,用递归来遍历整个树,将对应的状态恢复到每一个控件上。象 LoadPageStateFormPersistenceMedium 这样的方法和与其相对应的 SavePageStateToPersistenceMedium 方法可以用来加载或者保存视图状态到其他的存储中介里,例如:会话、数据库或者是服务器上的文件。和 LoadViewState 方法不相同的是,上面提到的方法只能在 Page 的继承类里使用。
3 处理回发数据:还原了视图状态,页面树种的各个控件的状态就与浏览器上次呈现该页面时这些控件所处的状态相同。下一步需要更新这些控件的状态以发送给客户端。
回发数据处理阶段是各个控件有机会更新其状态,以便准确的反映相应的 HTML 元素在客户端的状态。例如,一个服务器 TextBox 控件对应的 HTML 元素是 <input type=text> ,在回发数据阶段, TextBox 控件将检索 <input> 标记的当前值并用它刷新其内部状态。每个控件负责从以发送的数据中提取相应值,并更新其某些属性。 TextBox 控件将更新 Text 属性,而 CheckBox 控件将刷新其 Checked 属性。服务器控件和 HTML 元素之间的匹配关系由二者的 ID 确定。
页框架将在每个提交数据的控件上实现 IpostBackDataHandler 接口,然后激发 LoadPostData 事件,通过页面解析发现实现了 IpostBackDataHandle 接口的控件,这样就能正确的回传数据更新控件状态。在识别控件时, ASP.NET 通过匹配控件的唯一标示符来更新正确的控件,该标识符具有名称值集和中的名称值对。这也就是在所有特定的页中每个控件都需要一个唯一标识符的原因之一。其他的步骤都由框架来完成,例如确定每个标识符在环境中是否唯一以及控件的基本属性等。
LostPostData 方法的原型如下:
Public virtual bool LoadPostData(string postDatakey, NameValueCollection postCollection)
PostDataKey 是标识控件的关键字,可以理解为控件的 ID,postCollection 是包含回发数据的集合,可以理解为视图状态值。该方法返回一个 bool 值,如果是 true ,则表示控件状态因回发而更改;否则返回 false 。页框架会更跟踪所有返回 true 的控件并在这些控件上调用 RaisePostDataChangeEvent 事件。
LoadPostData 方法是由 System..Web.WebControls.Control 定义的,而添加的每一个服务器控件也是从 System..Web.WebControls.Control 继承的,所以对于数据的回发处理并不需要干预。
Load 事件中,当然还有像设置控件属性等操作的发生。这个过程是整个生命周期中最重要、最主要的,你可以通过调用 OnLoad 来重载 Load 事件
ASP.NET页面生命周期描述 (已整理)
4 加载页面 Load :在回发数据处理阶段结束时,页面中的所有控件都根据客户端上所输入的更改来更新的状态。此时,对页面激发 OnLoad 事件。对于这个事件,相信大多数朋友都会比较熟悉,用 Visual Studio.Net 生成的页面中的 Page_Load 方法就是响应 Load 事件的方法,对于每一次请求, Load 事件都会触发, Page_Load 方法也就会执行。可以利用该方法执行一些页面初始化,例如准备好数据库的连接字符串。在事件引用中,为了提高性能,通常使用 Page 类的 IsPostBack 属性判断是不是数据回发。
5 回发更改通知 RaisePostDataChanged :如( 3 )所述,在所有实现了 IpostBackDataHandler 接口的控件被正确的回传数据更新后,每个控件都有一个布尔值的标识,标识其自上一次提交后改控件的数据是被更改还是保持其值。然后 ASP.NET 通过搜索页来寻找任何显示控件数据被更改的标识并激发 RaisePostDataChanged RaisePostDataChanged 事件直到 Load 事件发生后,所有控件被更新后才激发。这保证了在控件被回传数据更新前,其他控件的数据在 RaisePostDataChanged 事件中没有被手动更改过。虽然也可以在 Page 的基础上自己定义数据更改的事件,但通常这个事件由太大用处。
6 处理回发事件 RaisePostBackEvent :当回传更新导致数据改变而引发服务器端事件后,引发回传的对象会在 RaisePostBackEvent 事件中被处理。这种引发回传的对象往往是一个按钮被单击或者其状态改变而引发回传的控件。例如 Button 触发乐 Onclick 事件、客户端修改了某个文本框的文本、同时将 AutoPostBack 设置为 true 、触发 TextChanged 事件等。
很多代码都在这个事件中执行,因为这是控制事件驱动逻辑的理想位置。为了保证呈现到浏览器的数据的正确性,在一系列的回传事件后, RaisePostBackEvent 事件最终被激发。基于一致性考虑,会传中改变的控件直到这个函数被执行后才被更新。在实际的 ASP.NET 开发工作中要做的工作就是在此事件发生前处理代码。
7 预呈现 PreRender :在处理回发事件后,页面就准备进行呈现。这一阶段的标志是 PreRender 事件。各个控件可利用这个很好的时机,以便执行任何需要在保存视图状态和呈现输出结果的前一刻完成得最后一些更新操作。最终请求的处理都会转变为发挥服务器的响应,预呈现这个阶段就是执行在最终呈现之前所做的状态的更改,因为在呈现一个控件之前,必须更具它的属性来产生 HTML, 比如 Style 属性。这是典型的例子,这预呈现之前,可以更改一个控件的 Style ,当执行预呈现时,就可以把 Style 保存下来,做为呈现阶段显示 HTML 的样式信息。
8 保存状态 SaveViewState :下一个状态为 SaveViewState ,在这一状态中所有控件以及页面本身可以刷新自己的 SaveState 集合的内容。所得到的视图状态随后得以序列化、进行哈希运算、进行 Base64 编码并关联到 VI-EMSTATE 隐藏自端。
9 呈现视图 Render :到这里,实际上页面对请求的处理基本就告一段落了,在 Render 事件中,也调用对象是它们呈现为 HTML ,然后也收集 HTML 发送给客户。客户接收到 HTML 标记后进行重组,最终显示给客户。当 Render 事件被重载时,开发者可以为浏览器创建定值的 HTML ,此时页面创建的任何 HTML 都还没有生效。 Render 方法用 HtmlTextWriter 对象做参数并由它产生 HTML 送给浏览器。这主要用于自定义控件的开发。
这时仍然可以做一些修改动作,不过它们只是客户端的一些变化而已了。你可以重载 Render 事件
ASP.NET页面生命周期描述 (已整理)
10 处置 Disposed :执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如内存的退出、数据库的连接等。
11 卸载 Unload :一个页面的最后生存标志就是 Unload 事件,该事件在页面对象被解除之前发生。在此事件中,可以调用 Dispose 方法尽可能释放占用的任何关键资源(例如,文件、图形对象以及数据库连接)。
举例说明 :
1. Page_Init();
这个过程主要是初始化控件 , 每次页面载入执行这个初始过程 , 包括第一次和以后的 Postback( 这里说下 Postback, 其实就可以简单理解成用户点击 SUBMIT 按钮之类的 , 把表单 <Form> 提交给服务器 , 这就是一次 postback), 在这里面可以访问控件 , 但是这里面的控件值不是我们期待的控件里面的值 , 他只是一个控件的初始值 ( 默认值 ), 举例 : 比如一个 TextBox1, 我们填入了 " 哈哈 ", 在点击 SUBMIT 提交了页面后 , Page_Init() 里面 , 我们访问到的 TextBox1.Text 不是我们的 " 哈哈 ", 而是开始的 "" 空字符串 , 如果 TextBox1 在我们设计的时候提供了默认值,这里访问到的也就是提供的默认值 , 为什么呢 , 这就要看下一个过程了 .
对应的事件 Page.Init
2. Load ViewState
这个过程是载入 VIEWSTATE Postback 数据,比如我们上面的 TextBox1, 这时就赋了 " 哈哈 ", 所以 , Post_Init() 对控件赋值是无意义的 , 它都会在这个过程里被改写 , 当然第一次页面载入例外,因为没有 VIEWSTATE 数据。
没有对应的事件
3.Load Postback data;
上面说了 ,Postback 可以理解成用户提交表单数据 , 所以这里就是处理表单数据 , 当然这里要设计到控件的设计 , 一般情况不会要我们自己处理这个过程 , 我们暂且略过 .
没有对应的事件
4. Page_Load();
这个过程也是每次页面载入时一定会执行的,但是注意和 Page_Init 的区别,上面已经涉及了 , 这里注意的是一般都会用到 Page.IsPostBack, 该值指示该页是否正为响应客户端回发而加载,或者它是否正被首次加载和访问。
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack)
{
//
第一次执行的 CODE HERE
}
else
{
//
用户提交 FORM( Postback)CODE HERE
}
// 每次这里的都回执行 CODE HERE
}
对应的事件 Page.Load
5. Handle control events;
这个过程里,相应具体的控件事件,比如 private void ListBox1_SelectedIndexChanged(object sender, System.EventArgs e) 事件等等
没有对应的事件 ( 我们自己的事件函数都包括在这个过程里比如上面的 ListBox1_SelectedIndexChanged)
6. Page_
预先呈递对象 , 这里是在向用户程序呈现数据的倒数第二步 , 我估计提供这个过程的意义 , 也就是在这里能对控件属性等等要呈现给用户的数据进行修改 , 这也是最后的修改 , 以前的修改 ( 比如在 Page_Init 里面 ) 都可能被覆盖 . 做完这了还会进行一个操作就是保存状态 , SaveViewState.
对应的事件时 Page.PreRender
7. Page_Render();
大家可以在浏缆器里 View->Source 查看到,每个页面都有一个隐藏的 <input>, 这里面的 "__VIEWSTATE" 就是我们服务器写回来的页面状态信息 , 在这个之前,服务器要呈现页面 ( 也就是构造 HTML 格式的文件 ) ,就是从这个 "__VIEWSTATE" 里面获取的数据 , 当然大家也注意到了
分享到:
评论

你可能感兴趣的:(数据结构,.net,应用服务器,asp.net,asp)