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我们可以看到:
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方法中:
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文件,代码如下:
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页面代码如下:
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 <