对由 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
方法被自动识别并被关联到相关的事件。
实例化
(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
文件中指定的一些控件,那么这些控件就没有属性可言了。同时,通过代码访问它们会产生一些意外的错误,因为这些控件实例是没有一个确定的创建顺序的(如果它们是被一起创建的)。
(2)
加载视图:在初始化之后,页面框架立即加载该页面的视图状态(
ViewState
)。所谓视图状态就是一些名称
/
值对的集合,例如可以保存
TextBox
控件的
ID
和
Text
属性值。它一般被用于在一个往返行程中存留信息到服务器,即参与
HTTP
请求与响应。
这种方式比起
Asp3.0
的维持、判断页面状态的方式有了很大的进步啊。还有,你可以重载
LoadViewState
事件函数来对相应的控件进行值设定。下图(图
2
)是一个例子:
页面视图状态被存储在
<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
事件
(
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
事件
(
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"
里面获取的数据
,
当然大家也注意到了
分享到:
评论