项目终于接近尾声了,二个不眠之夜后,终于可以抽出时间写点东西了,开发项目这么久了,回过头再一看,恍如南柯一梦,原来asp.net还可以这样来理解,原来自定义控件有这样大的作用,我将按照以下的技术点详细讲解:
(1).asp.net的内部运行机制。(不清楚此步,则自定义控件时会无法下手)。
(2).委托和事件。(如果自定义控件有回发时)
(3).Control类和WebControl类(通过多国语言标签控件说明)
(4)..何用使用Javascript.(包含一系列样式和客户端功能)
(5).自定义控件的样式和属性活用。(通过一个验证控件说明)
(6).复合控件(通过项目中最常的复合控件说明)
(7).ViewState的深度理解与疑问。
(8).模板控件。
(9).数据绑定控件。
好了,言规正传,下面我们来看看asp.net到底是如何运行的。
1.当IIS收到客户端的请求的时候,此时aspnet_wp这个进程会将要求转送到AppDomain的ISAPIRuntime对象,此对象解出必要的信息后,紧接着会调用HttpRuntime.ProcessRequest函数处理用户请求,我们反编译了HttpRuntime.ProcessRequest,看看核心代码,为了讲解的方便,以下仅列举核心代码:
2 {
3 // 检查当前调用者有没有作为ASP.NET宿主(Host)的权限
4 InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand();
5
6 if(wr == null)
7 {
8 throw new ArgumentNullException("custom");
9 }
10
11 RequestQueue queue = HttpRuntime._theRuntime._requestQueue;
12
13 if(queue != null)
14 {
15 // 将参数中的Web页面请求放入请求队列中
16 // 并从队列中使用FIFO策略获取一个页面请求
17 wr = queue.GetRequestToExecute(wr);
18 }
19
20 if(wr != null)
21 {
22 // 更新性能计数器
23 HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr);
24 // 实际完成页面请求工作
25 HttpRuntime.ProcessRequestNow(wr);
26 }
27 }
28
29
HttpRuntime.ProcessRequestNow函数则直接调用缺省HttpRuntime实例的ProcessRequestInternal函数完成实际页面请求工作,看以下代码:
{
// 构造 HTTP 调用上下文对象
HttpContext ctxt = new HttpContext(wr, 0);
//
// 获取当前 Web 应用程序实例
IHttpHandler handler = HttpApplicationFactory.GetApplicationInstance(ctxt);
// 调用 handler 实际处理页面请求
}
2.通过上面的代码我们已经很清楚了,HttpRuntime 在处理页面请求之前,根据 HttpWorkerRequest 中给出的环境,构造 HttpContext 对象,并以次对象作为参数从应用程序池中获取可用应用程序。经过此步骤,HttpContext对象被创建。
3.在创建HttpContext对象后,随之被创建的是HttpApplication对象,HttpRuntime并没有确定它将要处理请求的HttpApplication对象的类型,它调用了一个静态的工厂方法HttpApplicationFactory.GetApplicationInstance,GetApplicationInstance使用HttpContext实例来确定针对这个请求该响应哪个虚拟路径,如果这个虚拟路径以前请求过,HttpApplication(或者一个继承于ASP.Global_asax的类的实例)将直接从应用程序池中返回,否则针对该虚拟路径将创建一个新的HttpApplication对象并返回,下面我们来看代码看是否如此:
2
// 处理定制应用程序
3 if (HttpApplicationFactory._customApplication != null)
4
{
5 return HttpApplicationFactory._customApplication;
6 }
// 处理调试请求
7 if (HttpDebugHandler.IsDebuggingRequest(context))
8
{
9 return new HttpDebugHandler();
10 }
// 判断是否需要初始化当前 HttpApplicationFactory 实例
11 if (!HttpApplicationFactory._theApplicationFactory._inited)
12
{
13 lock (HttpApplicationFactory._theApplicationFactory)
14
{
15 if (!HttpApplicationFactory._theApplicationFactory._inited)
16
{
17 HttpApplicationFactory._theApplicationFactory.Init(context);
18 HttpApplicationFactory._theApplicationFactory._inited = true;
19 }
20 }
21 }
// 获取 Web 应用程序实例
22 return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);
23}
看到了吗,HttpApplicationFactory.GetApplicationInstance(ctxt),就是这个方法构造了web应用程序实例,我们看它的原型:
2 {
3 HttpApplication app = null;
4
5 // 尝试从已施放的 Web 应用程序实例队列中获取
6 lock(this._freeList)
7 {
8 if(this._numFreeAppInstances > 0)
9 {
10 app = (HttpApplication)this._freeList.Pop();
11 this._numFreeAppInstances--;
12 }
13 }
14
15 if(app == null)
16 {
17 // 构造新的 Web 应用程序实例
18 app = (HttpApplication)System.Web.HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
19
20 // 初始化 Web 应用程序实例
21 app.InitInternal(context, this._state, this._eventHandlerMethods);
22 }
23
24 return app;
25 }
26
27
4.至此一个 Web 应用程序实例就被完整构造出来,再经过InitInternal函数的初始化(包括调用 HttpApplication.InitModules 函数初始化 HTTP 模块(后面将详细介绍),并将作为参数传入的 HttpContext 实例保存到 HttpApplication._context 字段中。而此 HTTP 上下文对象将被后面用于获取会话对象),就可以开始实际页面处理工作了。
注意,这里的HTTP模块指的是Session,Authentication等模块。(稍后再详解)
5.为了表达方便,以上代码按顺序起名:代码一,代码二,.....................,请看代码二,当取得HttpApplication对象后,HttpApplication实例需要一个Handler对象来处理资源请求,主要任务就是找到真正处理请求的类。HttpApplication首先确定了一个创建Handler对象的工厂,来看一下在Machine.config文件中的配置区<httphandlers>,在配置文件注册了应用程序的具体处理类。例如在Machine.config中对*.aspx的处理将映射到System.Web.UI.PageHandlerFactory 类,而对*.ashx的处理将映射到System.Web.UI.SimpleHandlerFactory 类,这两个类都是继承于IhttpHandlerFactory接口的具体类:
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory" />
</httpHandlers>
这个配置区建立了资源请求的类型和处理请求的类之间的一个映射集。如果一个.aspx页面发出了请求,将会调用System.Web.UI.PageHandlerFactory类,HttpApplication调用接口IHttpHandlerFactory中的工厂方法GetHandler来创建一个Handler对象。当一个名为sample.aspx的页面发出请求时,通过PageHandlerFactory将返回一个ASP.SamplePage_aspx对象(具体产品)