介绍
每当请求IIS容纳的ASPNET页时总是要把请求转交给了ASPNET HTTP 管道HTTP管道是一组被控对象这些对象按顺序处理请求并且把这些请求转换成一般HTML文本HTTP管道的入口是HttpRuntime 类ASPNET的底层结构为每一个应用程序域 ( AppDomain )的工作进程建立了一个这个类(HttpRuntime)的实例(注意一个工作进程只能支持一个正在运行的ASPNET应用域)
HttpRuntime 类从内部程序池中选择一个 HttpApplcation 对象并且在接收到请求的时候使它工作Http应用管理程序的主要工作是寻找这样的类使之能够处理请求例如当请求一个aspx资源时处理句柄就是一个从Page继承类的实例请求资源的类型和相关处理句柄的关系映射表被保存在应用程序的配置文件里更确切的说这个映射表就定义在nfig里的<httpHandlers>一节里但是应用程序能在nfig里对这个HTTP处理句柄映射列表进行重定义下面这行语句说明了定义aspx资源请求的处理句柄
<add verb=* path=*aspx type=SystemWebUIPageHandlerFactroy/>
一个扩展可以和一个句柄类联系起来更一般说是和一个句柄工厂类相联系在所有情况下负责处理请求的HttpApplication对象得到一个从IHttpHandler接口具体实现的对象如果是根据HTTP句柄来处理资源和相关处理类的关系则返回类是直接实现相关的接口的如果资源是绑定到一个句柄工厂的话将必须经过另外一个阶段具体实现IHttpHandlerFactory接口的句柄工厂类的GetHandler方法将返回一个基于IHttpHandler的对象
Http运行时怎么结束一个周期或关闭一个页面请求的进程呢?IHttpHandler接口的ProcessRequest方法拥有这个功能调用代表被请求页面的对象的该方法ASPNET底层结构打开一个进程来为浏览器产生输出
Page类
一个页面的HTTP处理句柄的类型取决于URL当这个URL被首次访问一个新的类将被构建并动态的编译成一个程序集一个分析aspx文件的进程从aspx文件中分离出这个类的代码在默认情况下这个类被加入到一个叫做asp的名字空间里并且把URL作为这个类的类名例如如果请求的URL是pageaspx则这个类就是ASPPage_aspx这个类名可以通过设置@Page预处理指令的ClassName属性来修改
HTTP句柄的基础类是Page类这个类定义了一组最小方法和属性集这些方法和属性被所有的页面处理句柄所共享Page类具体实现了IHttpHandler接口
在另外一种和上述相对应的情况中实际处理页面的句柄的基础类并不是Page类而是一个别的类当使用后代码模式时这个情况就发生了后代码是一种将C#或VBNET代码和页面分离的技术页面代码是一组事件处理句柄和其他一些方法的集合这些方法定义了页面的各种行为这些代码可以以内联形式用<script runat=server>标签定义或者你可以用外部类形式来写——这就是后代码模式后代码类是从Page类继承的但是具体化或者重新定义了一些其他的方法在指定了页面的后代码类后这个后代码类就作为HTTP处理句柄
在其他的情况下如果应用程序的配置文件中重定义了<pages>节的PageBaseType属性则HTTP处理句柄不是基于Page类的例如
<pages PageBaseType = ClassesMyOage mypage />
PageBaseType属性指明了包含页面处理句柄父类的类型和程序集来自Page类的这些类自动赋予一些通常或扩展的方法和属性的集合给处理句柄
页面生命周期
一旦HTTP页面处理句柄被明确的定义了ASPNET运行时调用处理句柄的ProcessRequest方法来处理请求通常没有必要改变Page类提供的执行方法
页面执行是从FrameworkInitialize方法开始的这个方法为页面构建控件树该方法是TemplageControl类的受保护并且是虚方法任何为aspx资源动态生成的句柄覆盖了该方法在这个方法里页面的所有控件树都被构建了
接下来ProcessRequest方法使页面经历了不同的几个阶段初始化加载视图状态信息回传数据加载页面代码和执行回传的服务器事件在这之后页面转换到了显示模式收集被更新的视图状态产生HTML代码并且传送到控制台最后页面卸载请求的全部服务结束了
在各个不同阶段里页面处理了与web控件相关程序员代码能够干预并解决一定问题的事件其间一些事件是专门为那些内嵌控件和不能在aspx代码级别处理的控件而设计的
一个页面要解决这样的事件它能明确的注册成为合适的句柄但是为了和原有的Visual Basic编程模式有后向兼容性ASPNET也支持了隐含事件的形式在默认情况下页面会寻找和事件相关的方法名如果找到和事件相匹配的方法这个方法就被认为是这种事件的处理程序ASPNET提供了六种专门的方法名他们是 Page_Init Page_Load Page_DataBind Page_PreRender 和 Page_Unload 这些方法这些方法在Page类中已经被定义过他们是相应事件的处理程序HTTP运行时将自动的将这些方法绑定到相关的页面事件而不需要程序员去编写把事件和方法联系起来的代码举个例子来说在下面的代码中 Page_Load方法和页面的加载事件相关联
thisLoad + = new EventHandler(thisPage_Load);
这种自动识别是被 @Page 预指令的AutoEventWireup 属性控制的如果这个属性被置false 应用程序必须显式声明和事件相关的方法不自动关联页面事件代码的页面执行起来会快一些是因为他们不需要在匹配上做过多的工作在Visual StudioNET 工程里可以把这个属性关闭掉但是默认设置是true这意味着Page_Load方法被自动识别并被关联到相关的事件
页面执行包含了下表中按顺序列出的几个阶段他们被标志成为应用程序级别的事件同时也可能是一些受保护重定义的方法
阶段
页面事件
可重定义的方法
页面初始化
Init
视图状态加载
LoadViewState
回传数据处理
控件里实现了IPostBackDataHandler接口的LoadPostData方法
页面加载
Load
回传数据变化检查
控件里实现了IPostBackDataHandler接口的RaisePostDataChangedEvent方法
回传事件处理
控件里定义的回传事件
控件里实现了IPostBackEventHandler接口的RaisePostBackEvent方法
页面预返回阶段
PreRender
页面返回阶段
Render
页面卸载阶段
Unload
上表中列出的阶段有的在页面级别是不可见的他们只是在服务器控件的作者编写继承于Page的类时会使用到Init Load PreRender Unload再加上定义在内嵌控件中的回传处理事件他们构成了页面的整个生命周期
各个阶段的执行
页面生命周期的第一阶段是初始化这个阶段被Init事件所描述这个事件在控件树被构建出来后执行换句话说当Init事件发生时所有在aspx文件中静态声明的控件被实例化并被赋予了默认值在Init事件中可以初始化任何的在页面生命周期里需要的设置例如在这个阶段控件可以加载外部的摸版文件或者是为事件建立处理句柄需要注意的是任何的视图状态信息在这个阶段里是不能用的
紧接着初始化结束后页面构架为页面加载视图状态视图状态是 名称/值 对的集合控件或页面在这里保存的数据在整个web请求过程中必须是稳固的视图状态代表着页面的上下文典型的它保存着页面上次在服务器上被执行时控件的状态视图状态在会话开始的第一个页面请求时是空的在默认情况下试图状态被保存在一个隐藏域里这个隐藏域是被自动添加到页面里的这个隐藏域的名称是 __VIEWSTATE如果覆盖了LoadViewState方法——在Control类里被声明为受保护的方法——组件开发者可以控制视图状态的保存和它是如何和内部状态形成映射
象LoadPageStateFormPersistenceMedium这样的方法和与其相对应的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接口(Button和LinkButton就是这样的情况)页面构架将调用RaisePostBackEvent方法这个方法的具体情况是取决于控件的类型的在上面提到的Button和LinkButton控件这个方法就将寻找Click事件处理程序
处理了回传事件之后页面就准备被发送出去了这个阶段是从PreRender事件开始的这对于控件来说那些需要在视图信息被保存与结果被发送之前这段时间里执行的动作这是一个很好的时机下一步就是SaveViewState 所有的空间和页面本身就把视图状态的集合内容保存起来接下来视图状态被串行化哈希编码Base 编码并且保存在__VIEWSTATE隐藏域里
各个控件的发送机制可以通过重定义Render方法来改变这个方法构建了一个HTML writer对象用它来为控件产生HTML代码对Page类里Render方法的默认执行包含了对所有成员控件的递归调用页面为每一个控件调用一次Render方法并缓沖HTML输出
页面生命周期的最后阶段是卸载事件这个事件在页面对象消失前被激发在这个事件里你应该把任何临界资源释放掉(例如文件图形对象数据库连接)
最后浏览器接收到了HTTP响应并且把页面显示出来
总结
ASPNET页面对象模型是一个有特点的新颖的模型因为它是基于事件机制的一个Web页由一些控件组成这些控件拥有丰富的基于HTML的用户接口同时通过事件和用户进行交互在Web应用程序上下文环境中构建一个事件模型是富有挑战性的使客户端产生的事件和服务器上的代码关联起来是令人惊异的这个过程是输出同HTML也一样是可见的只不过他们在需要的时候被恰当的修改
要了解页面生命周期的各个阶段页面对象是怎样实例化并被HTTP运行时所使用掌握这个模型是很重要的