IHttpHandler系列 (2) ASP.NET应用程序生命周期趣谈1,2,3,4

转自: http://www.cnblogs.com/GodSpeed/archive/2010/07/15/1777941.html

 

ASP.NET应用程序生命周期趣谈(四) HttpHandler和页面生命周期

PS:本文很长,建议倒杯水拿点儿干粮再回来看,谢谢。

 

在之前的三篇文章中,我们还算简明扼要的学习了 asp.net 的整个生命周期,我们知道了一个 Request 进来以后先去 ISAPI Filter ,发现是 asp.net 程序后又 ASPNET_ISAPI.dll 这个 ISAPI Extension 来进行处理。在 ASPNT_ISAPI 创建了 Worder Process 后,在管道中经过 HttpModule 的处理来到 HttpHander 的手中。

我们知道 P_Handler 程序员使用 乾坤大挪移 对页面进行了处理后又通过管道中的 HttpModule response 返回给了客户端。

那么,这么

l         所谓的 乾坤大挪移 到底是个什么功夫呢?

l         P_Handler 又是如何被调用如何消亡的呢?

l         我们自己是否也可以创建 HttpHandler ?

l         HttpHandler 和我们常用的 .ashx 又是什么关系呢?

我们就通过今天的简单讲述让大家对这些过程有个大致的了解。

 

从字面上我们也可以看出, HttpHandler 就是 handle http request )的,就像程序员,就是(做)程序的人员一样,所以 P_Handler 就是个典型的 HttpRequest 处理人员。我们也知道 IHttpHandler 接口,它就像是一个认证,任何通过了 IHttpHandler 认证(实现了这个接口)的人员( handler ),都可以处理 HttpRequest 。这个认证主要有两个内容(方法): 1 ProcessRequest 2 IsReusable

ASP.NET 默认有很多个 Handler ,它们处理了各式各样的 asp.net 文件,例如 .config .cs .aspx 等等。通过路径 C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/CONFIG/web.config 找到 web.config 文件我们可以发现系统预定义的 httphandler ,因为太多在这就不一一列举。

通过 < add path = "*.config " verb = "* " type = "System.Web.HttpForbiddenHandler " validate = "true " /> 我们可以知道我们项目中的 web.config 文件为什么不能访问了,原来是被 HttpForbiddenHandler 给屏蔽了,我估计它是直接返回了一个错误,这样我们就不能访问这些资源,同样的, .sitemap, .asax, ..cs, .csproj 等等我们熟悉的项目中的文件都是使用 HttpForbiddenHandler 来屏蔽掉的。

而但是通过 <add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="true" /> 我们也可以发现原来 .aspx 是通过 PageHandlerFactory 来处理的。那么 PageHandlerFactory 又是什么东西呢?

通过反编译 PageHandlerFactory 我们可以看到:

 

Code
 1   public   class  PageHandlerFactory : IHttpHandlerFactory2, IHttpHandlerFactory
 2   {
 3        //  Fields
 4        private   bool  _isInheritedInstance;
 5        //  Methods
 6        protected   internal  PageHandlerFactory();
 7        public   virtual  IHttpHandler GetHandler(HttpContext context,  string  requestType,  string  virtualPath,  string  path);
 8        private  IHttpHandler GetHandlerHelper(HttpContext context,  string  requestType, VirtualPath virtualPath,  string  physicalPath);
 9        public   virtual   void  ReleaseHandler(IHttpHandler handler);
10       IHttpHandler IHttpHandlerFactory2.GetHandler(HttpContext context,  string  requestType, VirtualPath virtualPath,  string  physicalPath);
11   }
12  

 

 

这个 page handler 的工厂通过 GetHandler 得到了一个 IHttpHandler ,这个 Handler 是什么呢?答案就在 GetHandlerHelper 方法中:

 

代码
 1   private  IHttpHandler GetHandlerHelper(HttpContext context,  string  requestType, VirtualPath virtualPath,  string  physicalPath)
 2       {
 3           Page page  =  BuildManager.CreateInstanceFromVirtualPath(virtualPath,  typeof (Page), context,  true true as  Page;
 4            if  (page  ==   null )
 5           {
 6                return   null ;
 7           }
 8           page.TemplateControlVirtualPath  =  virtualPath;
 9            return  page;
10       }
11  

 

我们可以清楚的看到,这个方法返回了一个Page wow ,原来 Page 类就是处理 .aspx 文件的 HttpHandler 啊,看到 Page 类的定义:

public class Page

 : TemplateControl

, IHttpHandler



{

}

我们更是确信无疑: System.Web.UI.Page 就是处理 .aspx 页面的那个程序员 P_Handler 。这种程序员很平凡,却是不可或缺的,因为他们承担着一线的大量工作任务。而 PageHandlerFactory 就是专门培训这种处理页面的程序员的培训机构。这种培训机构很多,也就培养了各种各样的 Handler ,而我们自己也可以创建自己的培训机构,培训自己的 HttpHandler ,只要通过 IHttpHandlerFactory 这个认证(接口实现)就可以了。 Page 类是我们最常用的 P_Handler 程序员,它就像 .net 初级程序员一样普遍,只不过它不像我们这样什么都会,它只处理页面(因为它在一个大公司,微软啊,分工多明确啊)。

微软有程序员,我们也可以有,那么接下来我们就自己创建一个 HttpHandler ,我们用它来 Handler 我们定义的后缀名 .godspeed 文件。这个过程分为三步:

首先,我们创建一个实现了 IHttpHandler 的类

然后,在 web.config 文件中添加一行文字以便让 server 知道该如何处理 .godspeed 后缀名文件

最后,我们在 IIS 中映射我们的新后缀名到 asp.net ,好让 IIS 可以把这个文件传给 ASP.NET 进行处理并返回我们期望的结果。好,开始做。

1,  创建名为 TestCustomHttpHandler 的网站

2,  添加 test.godspeed 文件到网站中

3,  创建 GodSpeedHttpHandler.cs 文件,代码如下:

 

代码
 1   public   class  GodSpeedHttpHandler:IHttpHandler
 2  
 3   {
 4  
 5             public  GodSpeedHttpHandler()
 6  
 7            {
 8  
 9            }
10  
11        #region  IHttpHandler Members
12  
13        public   bool  IsReusable
14  
15       {
16  
17            get  {  return   true ; }
18  
19       }
20  
21        public   void  ProcessRequest(HttpContext context)
22  
23       {
24  
25           HttpResponse response  =  context.Response;
26  
27           response.Write( " <html><body><h1>This is GodSpeed Handler! See, easy!</body></html> " );
28  
29       }
30  
31        #endregion
32  
33   }
34  
35  

 

 

4,  配置 web.config

< handlers >

< add name = "GodSpeedHttpHandler " verb = "* " path = "*.godspeed " type = "MyHandlers.GodSpeedHttpHandler "/>

</ handlers >

5,  映射到 IIS asp.net

 

6,  访问 test.godspeed

 

 

re

多简单啊 各位看官可是看明白了?不明白没关系,欢迎回复和拍砖。

 

而我们常用的 ashx 也一种非常简单的 handler ,它通常用来处理不需要回传的任务,比如生成动态图片等等,园子里有很多这样的示例,在这里就不详述了。

 

那么我们之前提到 Page 类只处理页面,它也是我们最最常用的,最最重要的 P_Handler 程序员,它究竟是如何工作呢?它的工作对我们的日常开发又有什么影响呢?在下一篇文章中我将着重讲解页面生命周期 也就是 Page 类的 ProcessRequest 都引发了哪些事件以及在这些事件中我们能做些什么 进行深入的探讨。

 

 

 

话说我们今天的重中之重:页面生命周期 ,说的就是 Page 类在处理页面的过程中都发生了哪些事件,而这些事件又是按照什么顺序发生的。 ASP.NET 的页面生命周期跟我们之前的理论讲解完全不同,它具有非常强大的实用性,所以我想通过一个小例子来进行解释和说明,以便让大家看起来更清晰和容易理解。因为 Page 类的 ProcessRequest 方法实现非常的复杂,我把代码反编译过来给大家看我觉得也没什么意义,所以我就着重给大家看一下整个页面的事件是以何种顺序 被引发的,以及在这些事件中,我们能做什么 。在这个过程中,我们主要关注如下问题:

1,    事件的执行顺序

2,    控件何时被初始化(我们什么时候能用它)

3,    ViewState 何时可用

4,    更改 MasterPage Theme

5,    在各个事件中还能干什么工作

 

按照如下步骤建立示例:

一, 我们创建一个 website

二, 加入两个 MasterPage 分别为 site.master map.master ,分别在上面加一个 label 进行说明: this is site/map master page.

三, 分别加入两个 theme BlueSkin RedSkin, 分别对 button 的背景色进行设置,一个是 Blue ,另外一个是 Red

四, default 页面中加入两个 label 和一个 button

五, Default 页面代码如下:

代码
  1     private   int  step  =   1 ;
  2        private   string  GetEventName( string  eventName)
  3       {
  4           step  +=   1 ;
  5            return  eventName;
  6       }
  7  
  8        protected   override   void  OnPreInit(EventArgs e)
  9       {
 10            base .OnPreInit(e);
 11           Response.Write(GetEventName( " pre init event, this is the  "   +  step  +   " th step! " ));
 12            // lblMessage.Text = "on pre init";
 13  
 14            this .MasterPageFile  =   " map.master " ;
 15            this .Theme  =   " BlueSkin " ;
 16            if  (lblMessage2  ==   null )
 17           {
 18               Response.Write( " <span style='color:red;'>Server control has not been initialed on pre init</span> " );
 19           }
 20            else
 21           {
 22               Response.Write( " <span style='color:red;'>Server control has been initialed here</span> " );
 23           }
 24            if ( this .ViewState.Count > 0 )
 25           {
 26               Response.Write( " ViewState can be used here, the ID is  " + ViewState[ " ID " ].ToString());
 27           }
 28  
 29           Response.Write( " <br/> " );
 30       }
 31        protected   override   void  OnInit(EventArgs e)
 32       {
 33            if  (lblMessage2  ==   null )
 34           {
 35               Response.Write( " <span style='color:red;'>Server control has not been initialed on pre init</span> " );
 36           }
 37            else
 38           {
 39               Response.Write( " <span style='color:red;'>Server control has been initialed here</span> " );
 40           }
 41            base .OnInit(e);
 42         
 43           Response.Write(GetEventName( " init event, this is the  "   +  step  +   " the step!    " ));
 44            if  (lblMessage2  ==   null )
 45           {
 46               Response.Write( " <span style='color:red;'>Server control has not been initialed on pre init</span> " );
 47           }
 48            else
 49           {
 50               Response.Write( " <span style='color:red;'>Server control has been initialed here</span> " );
 51           }
 52            if  ( this .ViewState.Count  >   0 )
 53           {
 54               Response.Write( " <span style='color:red;'>ViewState can be used here, the ID is  "   +  ViewState[ " ID " ].ToString()  +   " </span> " );
 55           }
 56           Response.Write( " <br/> " );
 57  
 58            // this.MasterPageFile = "map.master";
 59            // this.Theme = "BlueSkin";
 60       }
 61        protected   override   void  OnInitComplete(EventArgs e)
 62       {
 63            base .OnInitComplete(e);
 64           Response.Write(GetEventName( " init complete event, this is the  "   +  step  +   " th step! " ));
 65            if  ( this .ViewState.Count  >   0 )
 66           {
 67               Response.Write( " <span style='color:red;'>ViewState can be used here, the ID is  "   +  ViewState[ " ID " ].ToString()  +   " </span> " );
 68           }
 69            else
 70           {
 71               Response.Write( " <span style='color:red;'>ViewState can not be used here</span> " );
 72           }
 73           Response.Write( " <br/> " );
 74       }
 75        protected   override   void  OnPreLoad(EventArgs e)
 76       {
 77            if  ( this .ViewState.Count  >   0 )
 78           {
 79               Response.Write( " <span style='color:red;'>ViewState can be used here, the ID is  "   +  ViewState[ " ID " ].ToString()  +   " </span> " );
 80           }
 81            else
 82           {
 83               Response.Write( " <span style='color:red;'>ViewState can not be used here</span> " );
 84           }
 85            base .OnPreLoad(e);
 86           Response.Write(GetEventName( " pre load event, this is the  "   +  step  +   " th step! " ));
 87            if  ( this .ViewState.Count  >   0 )
 88           {
 89               Response.Write( " <span style='color:red;'>ViewState can be used here, the ID is  "   +  ViewState[ " ID " ].ToString()  +   " </span> " );
 90           }
 91            else
 92           {
 93               Response.Write( " <span style='color:red;'>ViewState can not be used here</span> " );
 94           }
 95           Response.Write( " <br/> " );
 96       }
 97        protected   void  Page_Load( object  sender, EventArgs e)
 98       {
 99           Response.Write(GetEventName( " page load system provided, this is  "   +  step  +   " th step! " ));
100            if  ( this .ViewState.Count  >   0 )
101           {
102               Response.Write( " <span style='color:red;'>ViewState can be used here, the ID is  "   +  ViewState[ " ID " ].ToString()  +   " </span> " );
103           }
104           Response.Write( " <br/> " );
105       }
106  
107        protected   override   void  OnLoadComplete(EventArgs e)
108       {
109            base .OnLoadComplete(e);
110           Response.Write(GetEventName( " on load complete event, this is the  "   +  step  +   " th step!<br/> " ));
111       }
112  
113        protected   override   void  OnPreRender(EventArgs e)
114       {
115            base .OnPreRender(e);
116           Response.Write(GetEventName( " pre render event, this is the  "   +  step  +   " th step!<br/> " ));     
117       }
118  
119        protected   override   void  OnPreRenderComplete(EventArgs e)
120       {
121            base .OnPreRenderComplete(e);
122           Response.Write(GetEventName( " pre render complete event, this is the  "   +  step  +   " th step!<br/> " ));
123       }
124  
125        protected   override   void  OnSaveStateComplete(EventArgs e)
126       {
127          
128            base .OnSaveStateComplete(e);
129           Response.Write(GetEventName( " sae state complete event, this is the  "   +  step  +   " th step!<br/> " ));
130       }
131  
132        protected   override   void  Render(HtmlTextWriter writer)
133       {
134            base .Render(writer);
135           Response.Write(GetEventName( " render function, this is the  "   +  step  +   " th step!<br/> " ));
136       }
137  
138        protected   override   void  OnUnload(EventArgs e)
139       {
140            if  ( this   ==   null )
141           {
142                string  aaa  =   string .Empty;
143           }
144            if  (lblMessage2  ==   null )
145           {
146                string  ga  =   string .Empty;
147           }
148            base .OnUnload(e);
149            if  ( this   ==   null )
150           {
151    
152           }
153       }
154  
155        protected   override   void  OnLoad(EventArgs e)
156       {
157            base .OnLoad(e);
158           Response.Write(GetEventName( " page load we created, this is the  "   +  step  +   " th step!<br/> " ));
159       }
160        protected   void  btnSubmit_Click1( object  sender, EventArgs e)
161       {
162           ViewState[ " ID " =   " 12345 " ;
163           Response.Write(GetEventName( " button control click event, this is the  "   +  step  +   " th step!<br/> " ));
164       }

 

六,执行结果:

 

七, 代码分析

a)         从执行结果我们可以看到,事件的执行顺序为:

                         i.              PreInit

                       ii.              Init

                      iii.              InitComplete

                      iv.              PreLoad

                       v.              Load

                      vi.              Control Event(if they have)

                    vii.              LoadComplete

                   viii.              PreRender

                      ix.              PreRenderComplete

                       x.              SaveStateComplete

                      xi.              Render

                    xii.              Unload

 

b)         从结果中我们也可以看到,在 PreInit 事件发生后控件还没有被初始化,也就是我们还不能使用。而在 Init 刚刚发生的时候已经可以使用了。也就是说,控件初始化发生在 PreInit 之后 Init 之前。总而言之,我们关心的是我们最早可以在 Init 事件里对页面 server 端控件进行操控。

c)         关于 ViewState ,本示例也清晰的显示出它在 Init Complete 之后还是不能使用,而在 PreLoad 刚刚发生的时候,就已经可以使用了。

d)         通过对比 Default.aspx 页面的配置我们知道,在 PreInit 里面的 Theme MasterPage 的设置是生效了的,我们可以在 PreInit 里设置 Theme MasterPage ,而在其它任何位置设置这两个东西都将抛出异常(看我注视掉的代码)

e)         其它的事件:

Load : 这个事件可能是大家最熟悉的了。需要注意的是, Page 对象会递归的调用子控件的 onload 事件直到页面和所有的子控件被加载完成。这个事件主要用来设置控件属性的值,建立数据库连接(通常不这么做)。

Control events : 这个就不多说了,主要是处理控件的事件,例如 click 。这也就让我们明白了每次我们 click 一个 Button 的时候,实际上是要先去执行 load 事件然后才执行 click 事件的,一般我们用 !IsPostBack 来判断一下从而避免执行不必要的加载逻辑。

LoadComplete : 页面所有的控件都被加载以后执行,暂时没有想到用来干什么。。。

PreRender : HTML 被生成之前这是最后一个事件。每一个页面中的控件都有 PreRender 的过程。在这里对将要输出的 HTML 结果进行最后一次修改。

SaveStateComplete : 在这个时间发生之前,已经保存了所有控件和页面的,任何对 page 或者控件的改动都不会产生左右。暂时没想到用来干啥。

Render : 它不是一个事件而是一个方法。工作就是把 HTML 写回客户端浏览器。

UnLoad : 页面中的每一个控件都会发生这件事。在控件中,使用这个事件来做清理工作,例如关闭数据库连接等。对与页面本身也是做清理工作,例如关闭打开的文件和数据库连接,或者结束日志或者其它指定的工作。

f)          关于 Unload 事件,它实际上做的是销毁前 的工作。在 Unload 事件发生以后, Page 类被卸载和销毁,所以 page 类的字段值也就消失了,而我们通常也是使用在 SaveStateComplete 之前保存的 ViewState 来存储哪些我们想要存储的 page 里的数据。 HttpRuntime 做了销毁 Page 的动作,同样也是它创建的 Page 这个 handler ,现在我们知道原来不是 HttpApplication 雇佣了 P_Handler, HttpApplication 只是使用了它而已,真正雇佣(创建)并解雇(销毁) P_Handler 的是老板 HttpRuntime

 

整个 ASP.NET 生命周期基本结束,如果大家想更深入的了解其中内情,请反编译 System.Web.HttpRuntime 类。如果您不喜欢研究那么深入,我想我这几篇文章应该可以对您有些帮助。

 

我并不打算就此停手,接下来我还将会围绕 ASP.NET 生命周期和大家进一步深入探知 iis6 中的最重要的改进 —Application Pool 的奥秘和重要的 HttpModule—-SessionStateModule 。在这些都结束以后,我将接着跟大家学习 ASP.NET IIS7 中的变化及相应的知识,欢迎大牛指导和拍砖,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值