适用于:
Microsoft® ASP.NET
摘要:了解为 ASP.NET Web 页面建立的事件模型,以及 Web 页面转变为 HTML 过程中的各个阶段。ASP.NET HTTP 运行时负责管理对象管道,这些对象首先将请求的 URL 转换成 Page 类的具体实例,然后再将这些实例转换成纯 HTML 文本。本文将探讨那些作为页面生命周期标志的事件,以及控件和页面编写者如何干预并改变标准行为。(本文包含一些指向英文站点的链接。)
目录
简介
真正的 Page 类
页面的生命周期
执行的各个阶段
小结
简介
对由 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的方法绑定到页面的Load事件,则可省去以下代码。
this.Load += new EventHandler(this.Page_Load);
对特定名称的自动识别是由@Page指令的AutoEventWireup属性控制的。如果该属性设置为 false,则要处理事件的所有应用程序都需要明确连接到页面事件。不使用自动绑定事件的页面性能会稍好一些,因为不需要额外匹配名称与事件。请注意,所有 Microsoft Visual Studio® .NET 项目都是在禁用AutoEventWireup属性的情况下创建的。但是,该属性的默认设置是 true,即Page_Load等方法会被识别,并被绑定到相关联的事件。
下表中按顺序列出了页面的执行包括的几个阶段,执行的标志是一些应用程序级的事件和/或受保护并可覆盖的方法。
表 1:ASP.NET 页面生命中的关键事件
阶段
页面事件
可覆盖的方法
页面初始化 |
Init |
|
加载视图状态 |
|
LoadViewState |
处理回发数据 |
|
任意实现IPostBackDataHandler接口的控件中的LoadPostData方法 |
加载页面 |
Load |
|
回发更改通知 |
|
任意实现IPostBackDataHandler接口的控件中的RaisePostDataChangedEvent方法 |
处理回发事件 |
由控件定义的任意回发事件 |
任意实现IPostBackDataHandler接口的控件中的RaisePostBackEvent方法 |
页面显示前阶段 |
PreRender |
|
保存视图状态 |
|
SaveViewState |
显示页面 |
|
Render |
卸载页面 |
Unload |
|
以上所列的阶段中有些在页面级是不可见的,并且仅对服务器控件的编写者和要创建从Page导出的类的开发人员有意义。Init、Load、PreRender、Unload,再加上由嵌入式控件定义的所有回发事件,就构成了向外发送页面的各个阶段标记。
执行的各个阶段
页面生命周期中的第一个阶段是初始化。这个阶段的标志是Init事件。在成功创建页面的控件树后,将对应用程序触发此事件。换句话说,当Init事件发生时,.aspx 源文件中静态声明的所有控件都已实例化并采用各自的默认值。控件可以截取Init事件以初始化在传入的 Web 请求的生命周期内所需的所有设置。例如,这时控件可以加载外部模板文件或设置事件的处理程序。请注意,这时视图状态信息尚不可用。
初始化之后,页面框架将加载页面的视图状态。视图状态是名称/值对的集合,在此集合中,控件和页面本身存储了对所有 Web 请求都必须始终有效的全部信息。视图状态代表了页面的调用上下文。通常,它包含上次在服务器上处理页面时控件的状态。首次在会话中请求页面时,视图状态为空。默认情况下,视图状态存储在静默添加到页面的隐藏字段中,该字段的名称是__VIEWSTATE。通过覆盖LoadViewState方法(Control类的受保护、可覆盖方法),组件开发人员可以控制视图状态的存储方式以及视图状态的内容映射到内部状态的方式。
有些方法(如LoadPageStateFromPersistenceMedium以及其对应的SavePageStateToPersistenceMedium),可以用来将视图状态加载并保存到其他存储介质(例如会话、数据库或服务器端文件)中。与LoadViewState不同,上述方法只能在从Page导出的类中使用。
存储视图状态之后,页面树中控件的状态与页面最后一次显示在浏览器中的状态相同。下一步是更新它们的状态以加入客户端的更改。处理回发数据阶段使控件有机会更新其状态,从而准确反映客户端相应的 HTML 元素的状态。例如,服务器的TextBox控件对应的 HTML 元素是<input type=text>。在回发数据阶段,TextBox 控件将检索 <input> 标记的当前值,并使用该值来刷新自己内部的状态。每个控件都要从回发的数据中提取值并更新自己的部分属性。TextBox控件将更新它的Text属性,而CheckBox控件将刷新它的Checked属性。服务器控件和 HTML 元素的对应关系可以通过二者的 ID 找到。
在处理回发数据阶段的最后,页面中的所有控件的状态都将使用客户端输入的更改来更新前一状态。这时,将对页面触发Load事件。
页面中可能会有一些控件,当其某个敏感属性在两个不同的请求中被修改时,需要完成特定的任务。例如,如果TextBox控件的文本在客户端被修改,则此控件将触发TextChanged事件。每个控件在其一个或多个属性被修改为客户端输入的值时都可以决定触发相应的事件。对于这些更改对其非常关键的控件,控件实现IPostBackDataHandler接口,此接口的LoadPostData方法是在Load事件后立即调用的。通过对LoadPostData方法进行编码,控件将验证自上次请求后是否发生了关键更改,并触发自己的更改事件。
页面生命周期中的关键事件是被调用以执行服务器端代码的事件,此代码与客户端触发的事件相关联。当用户单击按钮时,将回发页面。回发值的集合中包括启动整个操作的按钮的 ID。如果控件实现IPostBackEventHandler接口(如按钮和链接按钮),页面框架将调用RaisePostBackEvent方法。此方法的行为取决于控件的类型。就按钮和链接按钮而言,此方法将查找Click事件处理程序并运行相关的委托。
处理完回发事件之后,页面就可以显示了。这个阶段的标志是PreRender事件。控件可以利用这段时间来执行那些需要在保存视图状态和显示输出的前一刻执行的更新操作。下一个状态是SaveViewState,在此状态中,所有控件和页面本身都将更新自己ViewState集合的内容。然后,将得到序列化、散列、Base64 编码的视图状态,而且此视图状态与隐藏字段 __VIEWSTATE 相关联。
通过覆盖Render方法可以改变各个控件的显示机制。此方法接受 HTML 书写器对象,并使用此对象来积累所有要为控件生成的 HTML 文本。Page类的Render方法的默认实现包括对所有成员控件的递归调用。对于每个控件,页面都将调用Render方法,并缓存 HTML 输出。
页面生命中的最后一个标志是Unload事件,在页面对象消除之前发生。在此事件中,您应该释放所有可能占用的关键资源(例如文件、图形对象、数据库连接等)。
在此事件之后,也就是最后,浏览器接收 HTTP 响应数据包并显示页面。
小结
ASP.NET 页面对象模型因其事件机制而显得格外新颖独特。Web 页面由控件组成,这些控件既可以产生丰富的基于 HTML 的用户界面,又可以通过事件与用户交互。以前,在 Web 应用程序的上下文中设置事件模型是件有挑战性的工作。可我们惊奇的看到,客户端生成的事件可以由服务器端的代码来解决,而且只进行一些相应的修改后,此过程仍可以输出相同的 HTML 页面。
掌握这个模型对于了解页面生命周期的各个阶段,以及页面对象如何被 HTTP 运行时实例化并使用是非常重要的。
关于作者
Dino Esposito是一位来自意大利罗马的培训教师和顾问。作为 Wintellect 团队的成员,Dino 专门研究 ASP.NET 和 ADO.NET,主要在欧洲和美国从事教学和咨询工作。此外,Dino 还负责管理 Wintellect 的 ADO.NET 课件,并为 MSDN 期刊的“Cutting Edge”专栏撰写文章。要与他联系,请向 dinoe@wintellect.com 发送电子邮件。
相关推荐
以下是ASP.NET页面加载顺序的详细解释: 1. **初始化阶段**: - **构造函数**:在页面被请求时首先调用,用于初始化自定义属性或对象。此时,可以访问`HttpContext`对象,如`QueryString`、`Form`、`Cookies`和`...
ASP.NET页面生命周期是ASP.NET应用程序开发中的核心概念,它描述了从用户请求页面到服务器响应完成的整个过程。了解这个生命周期对于高效、准确地编写ASP.NET代码至关重要。 1. **初始化对象** 在页面生命周期的...
页面生命周期是ASP.NET页面对象模型的核心概念,包括一系列事件,如初始化、加载、回发数据、验证、呈现和卸载。这些事件提供了开发者介入和控制页面行为的机会。例如,`Page_Load`事件用于在页面加载时执行特定...
ASP.NET页面生命周期同样复杂,每个页面在被请求时都会经历一系列步骤。以下是页面生命周期的主要阶段: 1. 浏览器请求:浏览器向服务器发送请求,可能包含POST数据。 2. 页面框架初始化:ASP.NET创建页面对象,初始...
ASP.NET的页面事件加载顺序不仅适用于基本页面,还适应于具有继承关系的页面。如果页面继承自BasePage或其他基类,事件的激活顺序会先执行基类的事件,然后才是派生类的事件。如果基类也继承自其他基类,那么事件的...
在ASP.NET中,每个网站实例都会创建一个或多个APPDOMAIN,用于加载和执行相关的代码。 ASP.NET程序的加载过程通常始于IIS(Internet Information Services),这是一个强大的Web服务器,负责接收HTTP请求并将其转发...
ASP.NET页面生命周期是ASP.NET Web Forms开发中的核心概念,它描述了一个ASP.NET页面从请求开始到响应返回给客户端的整个过程。在这个过程中,页面会经历一系列的阶段,每个阶段都有其特定的任务和目的,允许开发者...
ASP.NET页面加载事件顺序是开发ASP.NET Web应用程序时必须了解的关键概念。它涉及到网页生命周期中的各个阶段,每个阶段都有特定的事件,允许开发者在适当的时间点执行代码来处理用户请求和更新页面内容。理解这些...
以下是对Asp.NET页面事件加载顺序的详细解释: 1. **单独的Page事件顺序**: - `Page.PreInit`:这是页面生命周期的第一个事件,允许在此阶段设置控件属性,例如设置MasterPage。 - `Page.Init`:在此阶段,所有...
8. **页面生命周期和事件传播**:理解ASP.NET页面的生命周期对于优化性能和解决问题至关重要。PPT将解释各个阶段,如初始化、加载、回发等,以及事件传播顺序。 9. **部署和调试**:学习如何将ASP.NET应用程序部署...
ASP.NET页面有自己的生命周期,包括初始化、加载、验证、呈现和卸载等阶段。在ASP.NET第5章中,可能会详细解释每个阶段,以及如何在这些阶段中插入自定义代码。 5. **数据访问** ASP.NET提供了多种数据访问技术,...
从页面加载到呈现,每个阶段都有特定的任务和可以操作的时机。开发者需要理解何时初始化控件、绑定数据,以及如何进行页面间的状态传递。这有助于编写高效且响应式的Web应用。 书中还可能涉及了ASP.NET的视图状态...
以下是对ASP.NET页面周期的详细阐述: 1. **初始化阶段 (Initialization)**:在这个阶段,页面对象被创建,控件树被构造,控件的属性值被恢复(如果之前已保存)。这是可以修改控件属性或添加新控件的最早时机,但...
ASP.NET页面事件的顺序如下: - Page_Init:在这个阶段,页面初始化,对象实例化。 - Page_Load:所有控件加载完成,可以在此时进行数据绑定。 - 控件事件:如按钮点击等用户交互触发的事件。 - Page_Unload:...
ASP.NET页面生命周期包括初始化、加载、验证、呈现和卸载等阶段。在这些阶段,开发者可以插入代码以处理特定任务,如设置初始值、处理用户输入、保存状态等。 2. **控件事件处理**: ASP.NET控件支持各种事件,如...
2. **页面生命周期**:解释ASP.NET页面从请求到响应的生命周期,包括初始化、加载、回发和卸载等各个阶段。 3. **服务器控件**:介绍常见的ASP.NET服务器控件,如Label、TextBox、Button等,以及它们如何与后台代码...
使用ASP.NET的`Page_Load`事件来加载现有评论,`Button_Click`事件处理评论的提交。评论数据通常存储在数据库中,如SQL Server,因此我们需要编写ADO.NET代码或者使用Entity Framework等ORM工具来与数据库交互。 ...
10. **页面生命周期和事件模型**:理解ASP.NET页面从加载到呈现的完整生命周期以及事件触发顺序对于优化性能至关重要。源码可能包含了对页面生命周期和事件处理的实例。 11. **Ajax支持**:ASP.NET 2.0虽然没有内置...
3. **页面生命周期**:理解ASP.NET页面生命周期是关键,因为它决定了何时初始化数据,何时处理用户输入,以及何时呈现页面。在图片切换场景中,可能需要在Page_Load事件中加载图片数据,确保每次页面刷新时都能正确...