ASP.NET页面剖析(1)

在Web应用程序上下文中,ASP.NET页面会在第一次被请求时,按需被动态编译。动态编译并不是ASP.NET页面(.aspx文件)特有的,还发生在.NET Web服务(.asmx文件)、Web用户控件(.ascx文件)、HTTP处理程序(.ashx文件),以及其他几种ASP.NET应用程序文件(如global.asax文件)身上。运行时管道模型负责处理输入的(incoming)HTTP数据包,使其从一个简单的特定协议有效负载转化为服务器端ASP.NET对象——准确地说,是一个系统Page类的派生类的实例。ASP.NET中的HTTP运行库会处理页面对象,将生成的标记插入响应序列中。处理用户代码的若干事件标志着响应序列的生成各个阶段,整个过程称为“页面的生命周期”(page life cycle)。 本章会介绍如何将.aspx页面的HTTP请求映射为页面对象、Page类的编程接口,以及如何通过页面生命周期中的事件处理控制标记的生成。

 

页面的调用

先让我们具体认识一下.aspx页面是如何被转化为类并编译为程序集的。特定.aspx资源的程序集的生成,分为两个步骤进行。首先,该资源文件的源代码会被解析,根据得到的信息,从Page类(或Page的派生类)派生出相应的类。然后,动态生成的类会被编译为程序集,该程序集之后会被缓存到ASP.NET专用的临时目录下。

只要链接的(linked).aspx源文件没有被更改,且整个应用程序没有重启,已编译的页面就一直存在。对已链接的.aspx文件的任何更改,将使相关程序集变为无效,并在该页面下一次被请求时,强制HTTP运行库创建新的程序集。

提示:编辑web.config和global.asax之类的文件会导致整个应用程序重启。在这种情况下,在某个页面被请求时,所有页面会被重新编译。如果Bin文件夹中的程序集被改动(新建或被替换),所有页面也会被重新编译。

运行机制

能够通过基于Internet信息服务(IIS)的Web服务器访问的所有资源,会按扩展名进行组织。任何来访的请求会被分配给特定的运行时进程模块进行实际的处理。IIS上下文中能够处理Web资源的模块是Internet服务器应用程序编程接口(ISAPI)扩展——实际是普通的传统Win32动态链接库(DLL),很像接口,通过预定义的名称和原型,向外暴露了许多API函数。IIS和ISAPI扩展会针对专用的通信协议使用这些DLL入口方法。当IIS需要ISAPI扩展完成某项任务时,它会在是加载相应的DLL,并通过有效参数调用适当的函数。虽然ISAPI文档没有指出ISAPI为接口,但它的确是实现了一组公共编程接口的模块。

当某个资源的请求到达时,IIS首先会判断所请求的资源类型。静态资源(如图像、文本文件、HTML页面和非脚本ASP页面)直接由IIS处理,而不调用任何外部模块。IIS在本地Web服务器上访问被请求的文件,并将其内容写入输出控制台,这样,发出请求的浏览器便能够接收到它了。需要在服务器端处理的请求会被传到已注册的模块中。例如,ASP页面会由名为asp.dll的ISAPI扩展处理。一般而言,当资源与可执行代码关联时,IIS会将请求交给可执行代码做进一步处理。带.aspx扩展名的ISAPI扩展会被分配给aspnet_isapi.dll。

 资源映射信息存储在IIS元库(一种IIS专用的配置数据库)中。ASP.NET在安装时对IIS元库进行修改,使aspnet_isapi.dll能够处理某些典型的ASP.NET资源。

 aspnet_isapi.dll的IIS应用程序映射:

此外,aspnet_isapi.dll扩展还能够处理其他Microsoft Visual Studio典型扩展名(如.cs、.csproj、.vb、.vbproj、.config和.resx)。

IIS 6.0进程模型

IIS 6.0进程模型是ASP.NET的默认选择。该进程模型的名称已明确指出,该模型需要IIS 6.0。然而,在Windows Server 2003计算机上,仍可以使ASP.NET按IIS 5.0的方式工作。如果要这样做,可以通过更改machine.config的< processModel >区段,显式地启用该模型: <processModel enable="true">

注意,我们不提倡切换到过去的IIS 5.0进程模型(虽然这样做并没有错)。主要原因在于,IIS 6.0使用的是一种不同的内核模块管道来处理入站请求,只有在仿真模式下工作才能模仿IIS 5.0的行为。IIS 6.0管道以一种名为w3wp.exe的工作进程为中心。该可执行程序的副本由分配给同一应用程序池的所有Web应用程序共享。用IIS 6.0的术语来讲,应用程序池是共享同一工作线程副本的一组Web应用程序。IIS 6.0使我们能够对应用程序池进行定制,以达到托管于Web服务器的各种应用程序所需的隔离程度。 w3wp.exe工作进程会加载aspnet_isapi.dll,随后,ISAPI扩展加载公共语言运行时(CLR),启动ASP.NET运行时管道,对请求进行处理。若使用IIS 6.0进程模型,ASP.NET内建的工作进程便会被禁用。

提示:只有ASP.NET 1.1和更高版本能够充分利用IIS 6.0进程模型。如果在Windows Server 2003计算机上安装ASP.NET 1.0,那么选择默认设置IIS 5.0进程模型。之所以会这样,是因为只有ASP.NET 1.1搭载的aspnet_isapi.dll能够识别宿主,并在需要时加载CLR。而ASP.NET 1.0包含的aspnet_isapi.dll只能将请求发给ASP.NET工作进程,且不会加载CLR。

IIS 6.0中的ASP.NET应用程序和其他Web应用程序的处理过程:

IIS 6.0以内核级模块的形式实现了其HTTP监听程序。因此,所有的输入(incoming)请求会首先被一个驱动程序(http.sys)管理。第三方代码不能与该监听程序进行交互。这样,即使用户模式出现问题,IIS的稳定性也不会受到影响。http.sys驱动程序会监听请求,并将其追加到相应的应用程序池请求队列中。一个叫“Web管理服务”(Web Administration Service,WAS)的模块会读取IIS元库,并指示http.sys驱动程序创建请求队列,队列数量与元库中注册的应用程序池的数量一致。

总之,使用IIS 6.0进程模型,ASP.NET会运行得更快,因为inetinfo.exe(IIS 管理服务)与工作进程之间不需要进行任何进程间通信。HTTP请求直接投递给承托CLR的工作进程。此外,ASP.NET工作进程不是特殊的进程,而仅仅是IIS工作进程的副本。这样,回收进程、缓存页面和监视运行状况的负担会由IIS承受。
在IIS 6.0进程模型中,ASP.NET会忽略machine.config文件 <processModel>区段中的大部分内容,而只会从中读取线程和死锁设置。任何从元库读取的数据只能够使用“IIS管理器”进行配置(但其他配置信息仍会从.config文件中读取)。

被请求页面的表示

每个引用.aspx资源的输入请求都会被映射到Page的派生类。ASP.NET HTTP运行时环境首先会确定处理该请求的类名。页面的URL与类名会通过某种命名约定关联在一起。

例如,若请求的页面为default.aspx,则可推断出相关联的类名为ASP.default_aspx。如果当前加载到AppDomain的程序集中不包含带有这个名称的类,HTTP运行库将发出该类的创建和编译命令。该.aspx资源的源代码会被解析,以便创建该类的源代码,结果会临时保存在ASP.NET临时文件夹中。接下来,该类被编译并加载到内存中,以便处理该请求。当同一页面的请求再次到达时,由于该类已经存在,所以不会再次执行编译过程(只有当.aspx源代码被更改后,该类才会被重新创建并再次编译)。

请求的处理

为处理default.aspx页面,ASP.NET运行库需要获得ASP.default_aspx类的引用。如前所述,如果当前加载到AppDomain的程序集中没有该类,便创建它。随后,HTTP运行时环境通过公共接口IHttpHandler来调用该类。根类Page实现了该接口,它包含两个成员:ProcessRequest方法和Boolean类型的IsReusable属性。一旦HTTP运行库获得代表被请求资源的类的实例,便会调用ProcessRequest公共方法开始进行处理,以便向浏览器做出响应后终结。如前所述,调用并执行ProcessRequest及其所触发事件的整个过程统称为“页面的生命周期”。

虽然ASP.NET运行库是为页面服务的,但它生成标记代码的方式比其他平台更复杂,引入了许多对象。ASP.NET工作线程(无论是w3wp.exe还是aspnet_wp.exe)会将任何输入的请求交给HTTP管道。HTTP管道是一条完全可扩展的托管对象链,其工作方式与一般意义上的“管道”颇为相似。所有这些对象构成了所谓的ASP.NET HTTP运行时环境。

HttpRuntime对象

页面请求会传递给管道中的每一个处理原始HTTP有效负载的对象,在该链路的终端生成要发给浏览器的标记代码。HttpRuntime类就是该管道的入口点。对于输入的每个请求,ASP.NET工作线程通过创建HttpRuntime类的实例,并调用其ProcessRequest来激活该HTTP管道。要澄清一点,尽管HttpRuntime.ProcessRequest与IHttpHandler接口的名称意思相近,但二者实际并无关系。 HttpRuntime类包含许多私有和内部方法,但只公开了三个静态方法:Close、ProcessRequest和UnloadAppDomain。

 

HttpRuntime对象会在创建时对许多辅助处理页面请求的内部对象进行初始化。这些辅助对象包括缓存管理器和文件系统监视器(用于检测构成应用程序的文件的变动)。ProcessRequest方法被调用后,HttpRuntime对象即开始处理要发送到浏览器的页面。它会为请求创建一个新的上下文(空的),并初始化一个特殊的文本编写器(writer)对象,该对象用于缓存标记代码。上下文对象是HttpContext类的实例,它封装了所有与请求有关的HTTP特有的信息。
之后,这个HttpRuntime对象使用上下文信息查找或创建能够处理该请求的Web应用程序对象。通过包含在URL中的虚拟目录信息,便可定位Web应用程序。查找或新建应用程序的对象叫HttpApplicationFactory——它是一个内部使用的对象,负责返回能够处理该请求的有效对象。

 

posted @ 2010-02-08 16:38  GWPBrian  阅读(1664)  评论(1编辑  收藏  举报