ASP.NET Web 页面生命历程中的一天
发布日期 : 4/1/2004| 更新日期 : 4/1/2004
ASP.NET Web 页面生命历程中的一天
Dino EspositoWintellect
2003 年 8 月
适用于:Microsoft ASP.NET
摘要:了解围绕 ASP.NET Web 页构建的事件模型,以及一个 Web 页面在其转变为 HTML 的历程中的各个阶段。ASP.NET HTTP 运行时控制对象管线,对象管线首先将所请求的 URL 转换为一个页面类的活动实例,然后将其转换为普通 HTML 文本。本文将探讨一个页面的生存周期中的各个特征事件,并了解控件和页面编写者如何介入其中以改变其标准行为。(6 页打印页)
本页内容
简介 真正的 Page 类 页面生存周期 执行的各个阶段 小结
简介
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
真正的 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 处理程序的基类。
还有一种情况,即当应用程序配置文件的 <pages> 部分中重新定义了 PageBaseType 属性时,HTTP 处理程序也不是基于 Page 的。
<pages PageBaseType="Classes.MyPage, mypage" />
PageBaseType 属性指出了包含页面处理程序的基类的类型以及程序集。派生自 Page 的这个类可自动给处理程序赋予一组自定义和扩展的方法和属性。
页面生存周期
一旦完全确定 HTTP 页面处理程序类,ASP.NET 运行时就调用该处理程序的 ProcessRequest 方法以处理请求。通常情况下,无需更改此方法的实现方式,因为它是由 Page 类提供的。
此实现方法一开始就调用 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 方法 |
|
处理回发事件 |
控件所定义的任何回发事件 |
实现了 IPostBackEventHandler 接口的任何控件的 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 事件。
如果在处理两个不同的请求时某个敏感的属性被修改,则页面上可能有些控件需要完成某些任务。例如,如果在客户端修改了某个文本框控件的文本,则该控件激发 TextChanged 事件。如果利用来自客户端的值对该控件的一个或多个属性进行修改,每个控件可以决定激发一个适当的事件。对控件而言,如果这些更改是至关重要的,则这些控件实现 IPostBackDataHandler 接口,在 Load 事件之后立即调用该接口的 LoadPostData 方法。通过编写 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 Magazine的 "Cutting Edge" 专栏撰写文章。如果希望与 Dino 联系,可发送电子邮件至 dinoe@wintellect.com。
ASP.NET Web 页面生命历程中的一天相关推荐
- ASP.NET Web 页面生命中的一天
ASP.NET Web 页面生命中的一天 Dino Esposito Wintellect 2003 年 8 月 适用于: Microsoft® ASP.NET 摘要:了解为 ASP.NET Web ...
- ASP.NET Web Froms开发模式中实现程序集的延迟加载
延迟加载是一个很大的诱惑,可以达到一些比较好的效果,比如: 1.在实体框架中,由于关联数据的数量和使用时机是不确定的,通过延迟加载,仅在使用的时候去执行关联数据的查询操作,减少无谓的数据查询操作,可以 ...
- HTML页面转换asp,ASP.NET - Web 页面
ASP.NET - Web 页面 简单的 ASP.NET 页面看上去类似普通的 HTML 页面. Hello W3School 为了开始我们的 ASP.NET 学习之旅,首先我们将构造一张简单的 HT ...
- 在 ASP.NET Web 部件应用程序中使用服务器控件
在 Web 部件应用程序中,主要的用户界面(UI)由 Web 页面区域的 ASP.NET 服务器控件所组成,Web 页面拥有公共的 UI 以及派生自 WebPartZoneBase 类的合成控件.这些 ...
- 计算机网络——web页面请求历程
我们假设主机第一次连接到此网络,并且路由器作为DHCP服务器 准备:DHCP.UDP.IP和以太网 当一台机器连接到一个网络时,主机会将自身的MAC地址等其他自身信息,通过网络进行传播,目的地址使用的 ...
- ASP.NET Web页面(.aspx)添加用户控件(.ascx)无显示的问题
写好的用户控件点击视图显示没有问题,但是将控件添加到Web窗体上时却显示如下图标: F5运行,查看源代码:有代码--但是页面却显示一片空白. 分析代码,比较了一下能显示用户控件的其他web页面以及代码 ...
- 浅谈ASP中Web页面间的数据传递
[简 介] 基于Web的动态网页设计必会涉及到页面间的数据传递,文章探讨了ASP设计中常用的Web页面间的数据传递方式,分析各种数据传递方式的使用方法.使用场合及优缺点,其都是设计阶段选择数据传递方式 ...
- Asp.net2.0页面的生命周期(续)
以上就是Asp.net页面生命周期中的几个主要事件.每次我们请求一个Asp.net页面时,我们都经历着同样的过程:从初始化对象到销毁对象.通过了解Asp.net页面的内部运行机制,我相信大家在编写.调 ...
- ASP.Net请求处理机制初步探索之旅 - Part 4 WebForm页面生命周期
开篇:上一篇我们了解了所谓的请求处理管道,在众多的事件中微软开放了19个重要的事件给我们,我们可以注入一些自定义的业务逻辑实现应用的个性化设计.本篇,我们来看看WebForm模式下的页面生命周期. ( ...
最新文章
- python100个必背知识-学Python必须背的42个常见单词,看看你都会吗?
- java禁止ie后退_Java开发网 - 【讨论】html页面禁用右键,浏览器后退、刷新,以及相应快捷键...
- 分享一个WebGL开发的网站-用JavaScript + WebGL开发3D模型
- 微信公众平台网站开发JS_SDK遇到的bug——wx.config注册提示成功,但部分接口注册失败问题
- Python中raise用法
- 汇编:在BUFFER中定义了的十个带符号字,将其中的负数变成绝对值,并以十进制方式输出
- Highcharts柱形范围图使用示例
- Reinforcement Learning[论文合集]
- QT应用编程: 编写HC05串口蓝牙调试助手(Android系统APP)
- 李一男荡人心魄的戏剧化人生
- 打开应用商店显示服务器出错了,Win10应用商店提示“我们这边出错了”的三种解决方法...
- vivo手机便签app下载,vivo自带便签软件下载
- android 框架搭建养成的良好习惯(一)
- 数据结构之树家族介绍
- MATLAB 2018a安装教程(迅雷)
- #运算符 ##预算符
- 优雅地使用eruda在移动端上调试网页
- 智能安防视频监控平台页面无法访问该如何排查?
- cufflinks 绘图
- 南京公用,海得控制,上柴股份,成交量异动检测
热门文章
- JavaScript——原型/原型链中的顶层对象(图解)
- java环境配置 Windows10
- 计算机行业的pest分析,2014-2018年中国电脑外设行业市场发展现状及未来趋势调研报告...
- java面试题2021最新
- Hibernate 简介
- python3.4学习笔记(二十一) python实现指定字符串补全空格、前面填充0的方法
- C# 调用其他的动态库开发应注意的问题
- Infinity loop in cursor iteration
- Best Friend Forever
- 【HTTP】 认证和单点登录 【瞎写的…】