原文作者:Shivprasad koirala | |
原文地址:http://www.codeproject.com/Articles/73728/ASP-NET-Application-and-Page-Life-Cycle |
|
他的介绍:微软MVP(ASP/ASP.NET),现在是印度一家小型在线教育公司的CEO。他非常积极地在制作在线培训视频,写技术书籍及做企业培训。 |
- 概述
- 大体上的两步处理流程
- ASP.NET环境的创建
- 通过MHPM触发的事件处理请求
- 在什么事件中我们可以做什么?
- 一个简单的示例
- 详解ASP.NET页面事件
图2 ASP.NET环境的创建
下图则形象地展示了在一个ASP.NET请求过程中的重要内部对象模型。最高层是ASP.NET运行时,它创建了一个应用程序域(AppDoamin),下层则创建了一个包含request、response以及context对象的HttpRuntime。
图3 ASP.NET请求过程中的内部对象模型
四、通过MHPM触发的事件处理请求
一旦HttpApplication创建好,它就开始处理请求了。它经历了三个不同的部分:HttpModule、Page和HttpHandler。当它经过这些部分时,它将调用不同的事件,而这些事件的逻辑处理还可以由开发者来进行扩展和增加自定义处理。
在进一步深入了解之前,让我们先来了解一下什么是HttpModule和HttpHandlers。他们帮助我们在ASP.NET页面处理过程的前后注入自定义的逻辑处理。他们之间主要的差别在于:
- 如果你想要注入的逻辑是基于像'.aspx','.html'这样的扩展名,那么你可以使用HttpHandler。换句话说,HttpHandler是一个基于处理器的扩展。
图4 HttpHandler
- 如果你想要在ASP.NET管道事件中注入逻辑,那么你可以使用HttpModule。也可以说,HttpModule是一个基于处理器的事件。
图5 HttpModule
你可以从这里了解更多关于他们这对好基友之间的差别。
下面是请求处理过程的逻辑流程,其中有4个重要的步凑,解释如下:
第一步(M:HttpModule):客户端请求开始被处理。在ASP.NET引擎执行和创建HttpModule触发事件(在此过程中,你也可以注入自定义逻辑)之前,有6个事件你可以在页面对象创建之前来使用,它们分别是:BeginRequest、AuthenticateRequest、AuthorizeRequest、ResolveRequestCache、AcquireRequestState 以及 PreRequestHandlerExecute。
第二步(H:HttpHandler):一旦以上6个事件被触发后,ASP.NET引擎就将会调用 ProcessRequest 事件,即使你已经在项目中实现了 HttpHandler。
第三步(P:ASP.NET Page):一旦HttpHandler逻辑执行,ASP.NET页面对象就被创建了。而ASP.NET页面被创建,一系列的事件也会随之被触发,它们可以帮助我们自定义逻辑注入到这些事件里边。在此过程中,有6个重要事件给我们提供了占位符,以便我们在ASP.NET页面中写入逻辑,它们分别是:Init、Load、Validate、Render 和 Unload。你可以通过记住单词SILVER来记忆这几个事件,S—Start(没有任何意义,仅仅是为了形成一个单词),I(Init)、L(Load)、V(Validate)、E(Event)、R(Render)。
第四步(M:HttpModule):一旦页面对象执行结束并从内存中被卸载,HttpModule提供了提交返回页面的执行事件,同样,在这些事件中也可以被注入自定义的返回处理逻辑。这里有4个重要的提交处理事件:PostRequestHandlerExecute、ReleaserequestState、UpdateRequestCache以及EndRequest。
下图形象地展示了上面的四个步凑。
图6 MHPM过程
五、在什么事件中我们可以做什么?
一个十分有价值的问题就是在什么事件中我们又可以做些什么?下表就展示了这个问题的答案:
Section | Event | Description |
HttpModule | BeginRequest | 此事件标志着一个新的请求,它保证在每个请求中都会被触发。 |
HttpModule | AuthenticateRequest | 此事件标志ASP.NET运行时准备验证用户。任何身份验证代码都可以在此注入。 |
HttpModule | AuthorizeRequest | 此事件标志ASP.NET运行时准备授权用户。任何授权代码都可以在此注入。 |
HttpModule | ResolveRequest | 在ASP.NET中我们通常使用OutputCache指令做缓存。在这个事件中,ASP.NET运行时确定是否能够从缓存中加载页面,而不是从头开始生成。任何缓存的具体活动可以被注入这里。 |
HttpModule | AcquireRequestState | 此事件标志着ASP.NET运行时准备获得Session会话变量。可以对Session变量做任何你想要做的处理。 |
HttpModule | PreRequestHandlerExecute | 恰好在ASP.NET 开始执行事件处理程序前发生。可以预处理你想做的事。 |
HttpHandler | ProcessRequest | HttpHandler逻辑被执行。在这个部分我们将为每个页面扩展写需要的逻辑。 |
Page | Init | 此事件发生在ASP.NET页面且可以用来: 1、动态地创建控件,如果你一定要在运行时创建控件; 2、任何初始化设置 3、母版页及其设置 在这部分中我们没有获得viewstate、postedvalues及已经初始化的控件。 |
Page | Load | 在这部分ASP.NET控件完全被加载且在这里你可以写UI操作逻辑或任何其他逻辑。NOTE:这个事件也是我们最常见且最常用的一个事件。 |
Page | Validate | 如果在页面上你有验证器,你同样想在这里做一下检查。 |
Page | Render | 是时候将输出发送到浏览器。如果你想对最终的HTML做些修改,你可以在这里输入你的HTML逻辑。 |
Page | Unload | 页面对象从内存中卸载。 |
HttpModule | PostRequestHandlerExecute | 可以注入任何你想要的逻辑,在处理程序执行之后。 |
HttpModule | ReleaseRequestState | 如果你想要保存对某些状态变量的更改,例如:Session变量的值。 |
HttpModule | UpdateRequestCache | 在结束之前,你是否想要更新你的缓存。 |
HttpModule | EndRequest | 这是将输出发送到客户端浏览器之前的最后一个阶段。 |
六、一个简单的示例
我们可以通过一个示例程序代码来展示以上介绍的那些事件是怎样被最终触发的。在这个示例中,我们已经创建了一个HttpModule和HttpHandler,并且也在所有的事件中通过添加自定义逻辑代码展示了一个简单的响应。
下面是HttpModule类,它跟踪了所有的事件并将其添加到了一个全局的集合中。
public class clsHttpModule : IHttpModule { ...... void OnUpdateRequestCache(object sender, EventArgs a) { objArrayList.Add("httpModule:OnUpdateRequestCache"); } void OnReleaseRequestState(object sender, EventArgs a) { objArrayList.Add("httpModule:OnReleaseRequestState"); } void OnPostRequestHandlerExecute(object sender, EventArgs a) { objArrayList.Add("httpModule:OnPostRequestHandlerExecute"); } void OnPreRequestHandlerExecute(object sender, EventArgs a) { objArrayList.Add("httpModule:OnPreRequestHandlerExecute"); } void OnAcquireRequestState(object sender, EventArgs a) { objArrayList.Add("httpModule:OnAcquireRequestState"); } void OnResolveRequestCache(object sender, EventArgs a) { objArrayList.Add("httpModule:OnResolveRequestCache"); } void OnAuthorization(object sender, EventArgs a) { objArrayList.Add("httpModule:OnAuthorization"); } void OnAuthentication(object sender, EventArgs a) { objArrayList.Add("httpModule:AuthenticateRequest"); } void OnBeginrequest(object sender, EventArgs a) { objArrayList.Add("httpModule:BeginRequest"); } void OnEndRequest(object sender, EventArgs a) { objArrayList.Add("httpModule:EndRequest"); objArrayList.Add("<hr>"); foreach (string str in objArrayList) { httpApp.Context.Response.Write(str + "<br>") ; } } }
下面是HttpHandler类的一个代码片段,它跟踪了ProcessRequest事件。
public class clsHttpHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { clsHttpModule.objArrayList.Add("HttpHandler:ProcessRequest"); context.Response.Redirect("Default.aspx"); } }
同上,我们也可以跟踪来自ASP.NET Page页面的所有事件。
public partial class _Default : System.Web.UI.Page { protected void Page_init(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:Init"); } protected void Page_Load(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:Load"); } public override void Validate() { clsHttpModule.objArrayList.Add("Page:Validate"); } protected void Button1_Click(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:Event"); } protected override void Render(HtmlTextWriter output) { clsHttpModule.objArrayList.Add("Page:Render"); base.Render(output); } protected void Page_Unload(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:UnLoad"); } }
下图则显示了上面我们所讨论的所有事件的执行顺序。
图7 示例结果—事件的执行次序
七、详解ASP.NET页面事件
在上面的部分中,我们已经了解了一个ASP.NET页面请求事件的整体流程。那么,在其中一个最重要的部分就是ASP.NET页面,但是我们并没有对其进行详细讨论。因此,我们在此深入地了解一下ASP.NET页面事件。
每一个ASP.NET页都有2个部分:一个是在浏览器中进行显示的部分,它包含了HTML标签、viewstate形式的隐藏域 以及 在HTML input中的数据。当这个页面被提交到服务器时,这些HTML标签会被创建到ASP.NET控件,并且viewstate还会和表单数据绑定在一起。一旦你在后置代码中得到所有的服务器控件,你可以执行和写入你自己的逻辑并呈现给客户浏览器。
图8 ASP.NET页的两个部分
现在这些HTML控件会作为ASP.NET控件存活在服务器上,ASP.NET会触发一系列的事件,我们也可以在这些事件中注入自定义逻辑代码。根据你想要执行什么样的任务/逻辑,我们需要将逻辑合理地放入这些事件之中。
注意:大部分的开发者直接使用Page_Load来干所有的事情,但这并不是一个好的思路。因此,无论是填充控件、设置ViewState还是应用主题等所有发生在页面加载中的所有事情。因此,如果我们能够在合适的事件中放入逻辑,那么毫无疑问我们代码将会干净很多。
顺序 | 事件名称 | 控件初始化 | ViewState可用 | 表单数据可用 | 什么逻辑可以写在这里? |
1 | Init | No | No | No | 注意:你可以通过使用ASP.NET请求对象访问表单数据等,但不是通过服务器控件。 动态地创建控件,如果你一定要在运行时创建;任何初始化设置;母版页及其设置。在这部分中我们没有获得viewstate、提交的数据值及已经初始化的控件。 |
2 | Load View State | Not guaranteed | Yes | Not guaranteed | 你可以访问View State及任何同步逻辑,你希望viewstate被推到后台代码变量可以在这里完成。 |
3 | PostBackdata | Not guaranteed | Yes | Yes | 你可以访问表单数据。任何逻辑,你希望表单数据被推到后台代码变量可以在这里完成。 |
4 | Load | Yes | Yes | Yes | 在这里你可以放入任何你想操作控件的逻辑,如从数据库填充combox、对grid中的数据排序等。这个事件,我们可以访问所有控件、viewstate、他们发送过来的值。 |
5 | Validate | Yes | Yes | Yes | 如果你的页面有验证器或者你想为你的页面执行验证,那就在这里做吧。 |
6 | Event | Yes | Yes | Yes |
如果这是通过点击按钮或下拉列表的改变的一个回发,相关的事件将被触发。与事件相关的任何逻辑都可以在这里执行。 PS:这个事件想必很多使用WebForm的开发人员都很常用吧,是否记得那些Button1_Click(Object sender,EventArgs e)? |
7 | Pre-render | Yes | Yes | Yes | 如果你想对UI对象做最终的修改,如改变属性结构或属性值,在这些控件保存到ViewState之前。 |
8 | Save ViewState | Yes | Yes | Yes | 一旦对服务器控件的所有修改完成,将会保存控件数据到View State中。 |
9 | Render | Yes | Yes | Yes | 如果你想添加一些自定义HTML到输出,可以在这里完成。 |
10 | Unload | Yes | Yes | Yes | 任何你想做的清理工作都可以在这里执行。 |
图9 ASP.NET Page事件流程
一张图复习ASP.NET请求处理(自己补充,非原文内容)
翻译中参考的资料
(1)碧血轩,《ASP.NET页面生命周期》,http://www.cnblogs.com/xhwy/archive/2012/05/20/2510178.html
(2)吴秦,《ASP.NET 应用程序与页面生命周期(意译)》,http://www.cnblogs.com/skynet/archive/2010/04/29/1724020.html
(3)风尘浪子,《C#综合揭秘—细说进程、应用程序域与上下文之间的关系》,http://www.cnblogs.com/skynet/archive/2010/04/29/1724020.html
(4)菩提树下的杨过,《温故而知新:HttpApplication,HttpModule,HttpContext及Asp.Net页生命周期》,http://www.cnblogs.com/yjmyzz/archive/2010/03/28/1698968.html
(5)MSDN,《ASP.NET页面生命周期概述》,http://msdn.microsoft.com/zh-cn/library/ms178472.aspx
(6)皱华栋,《ASP.NET!=拖控件之2011版视频教程》,http://bbs.itcast.cn/thread-8439-1-1.html