【asp源码栏目提醒】:网学会员asp源码为您提供第3章 ASP NET页面剖析 - 技术总结参考,解决您在第3章 ASP NET页面剖析 - 技术总结学习中工作中的难题,参考学习。
1 第3章
ASP.NET页面剖析 在Web应用程序上下文中
ASP.NET页面会在第一次被请求时按需被动态编译。
动态编译并不是
ASP.NET页面.aspx文件特有的还发生在.NET Web服务.asmx文件、Web用户控件.ascx文件、HTTP处理程序.ashx文件以及其他几种
ASP.NET应用程序文件如global.asax文件身上。
运行时管道模型负责处理输入的incomingHTTP数据包使其从一个简单的特定协议有效负载转化为服务器端
ASP.NET对象——准确地说是一个系统Page类的派生类的实例。
ASP.NET中的HTTP运行库会处理页面对象将生成的标记插入响应序列中。
处理用户代码的若干事件标志着响应序列的生成各个阶段整个过程称为“页面的生命周期”page life cycle。
本章会介绍如何将.aspx页面的HTTP请求映射为页面对象、Page类的编程接口以及如何通过页面生命周期中的事件处理控制标记的生成。
3.1 页面的调用 先让我们具体认识一下.aspx页面是如何被转化为类并编译为程序集的。
特定.aspx资源的程序集的生成分为两个步骤进行。
首先该资源文件的源代码会被解析根据得到的信息从Page类或Page的派生类派生出相应的类。
然后动态生成的类会被编译为程序集该程序集之后会被缓存到
ASP.NET专用的临时目录下。
只要链接的linked.aspx源文件没有被更改且整个应用程序没有重启已编译的页面就一直存在。
对已链接的.aspx文件的任何更改将使相关程序集变为无效并在该页面下一次被请求时强制HTTP运行库创建新的程序集。
【89】 提示编辑web.config和global.asax之类的文件会导致整个应用程序重启。
在这种情况下在某个页面被请求时所有页面会被重新编译。
如果Bin文件夹中的程序集被改动新建或被替换所有页面也会被重新编译。
3.1.1 运行机制 能够通过基于Internet信息服务IIS的Web服务器访问的所有资源会按扩展名进行组织。
任何来访的请求会被分配给特定的运行时进程模块进行实际的处理。
IIS上下文中能够处理Web资源的模块是Internet服务器应用程序编程接口ISAPI扩展——实际是普通的传统Win32动态链接库DLL很像接口通过预定义的名称和原型向外暴露了许多API函数。
IIS和ISAPI扩展会针对专用的通信协议使用这些DLL入口方法。
当IIS需要ISAPI扩展完成某项任务时它会在是加载相应的DLL并通过有效参数调用适当的函数。
虽然ISAPI文档没有指出ISAPI为接口但它的确是实现了一组公共编程接口的模块。
2 当某个资源的请求到达时IIS首先会判断所请求的资源类型。
静态资源如图像、文本文件、HTML页面和非脚本
ASP页面直接由IIS处理而不调用任何外部模块。
IIS在本地Web服务器上访问被请求的文件并将其内容写入输出控制台这样发出请求的浏览器便能够接收到它了。
需要在服务器端处理的请求会被传到已注册的模块中。
例如
ASP页面会由名为
asp.dll的ISAPI扩展处理。
一般而言当资源与可执行代码关联时IIS会将请求交给可执行代码做进一步处理。
带.aspx扩展名的ISAPI扩展会被分配给aspnet_isapi.dll如图3.1所示。
图3.1 带.aspx扩展名的资源的IIS应用程序映射 【90】 资源映射信息存储在IIS元库一种IIS专用的配置数据库中。
ASP.NET在安装时对IIS元库进行修改使aspnet_isapi.dll能够处理某些典型的
ASP.NET资源。
表3.1列出了其中的一些。
表3.1 aspnet_isapi.dll的IIS应用程序映射 扩 展 名 资源类型 .asax
ASP.NET应用程序文件如global.asax。
这种映射能够确保global.asax不被直接通过请求而获得 .ascx
ASP.NET用户控件文件 .ashx HTTP处理程序能够与IIS低级请求/响应服务进行交互的托管模块 .asmx 实现.NET Web服务的文件 .aspx
ASP.NET页面文件 .axd 标识内部HTTP处理程序的扩展名用于实现某些系统功能如应用程序级的跟踪trace.axd或脚本注入webresource.axd等 此外aspnet_isapi.dll扩展还能够处理其他Microsoft Visual Studio典型扩展名如.cs、.csproj、.vb、.vbproj、.config和.resx。
3 正如第1章所述
ASP.NET ISAPI扩展的具体行为取决于应用程序所选择的进程模型。
下面将给出其中的两种。
IIS 5.0进程模型 如果将
ASP.NET应用程序部署到Windows Server 2003之前任何一个版本的系统中就只能选择IIS 5.0进程模型。
根据该进程模型的策略aspnet_isapi.dll不处理.aspx文件而是充当调度程序。
它将收集有关被调URL和底层资源的所有信息并将其发送给另一个特殊进程——名为aspnet_wp.exe的
ASP.NET工作进程。
ISAPI扩展与工作进程之间的通信是通过命名管道named pipe完成的。
整个模型如图3.2所示。
【91】 工作进程的一个副本始终运行并承托所有活动的Web应用程序。
但在Web服务器使用多个CPU时情况则不同。
如果这样可以对
ASP.NET运行库进行配置允许多个工作进程运行每个进程对应一个可用CPU。
一台服务器的多个CPU上运行多个进程这样的模型称为“网络园”web garden可通过machine.config文件的ltprocessModelgt区段控制。
当单一工作线程被所有CPU使用并控制所有Web应用程序时并不一定表示没有实施进程隔离。
事实上每个Web应用程序是通过其虚拟目录标识的分别从属于独立的应用程序域通常称为AppDomain。
当某个虚拟目录被客户端第一次请求时
ASP.NET工作进程会创建一个新的AppDomain。
之后
ASP.NET运行库将加载所有所需的程序集并将控制权交给托管hostedHTTP管道后者将对该请求做实际的处理。
【92】 如果客户端请求的是一个已在运行的Web应用程序
ASP.NET运行库只是将该请求转发给与其虚拟目录相关联的现有AppDomain。
如果处理当前页面的程序集没有被该AppDomain加载则动态创建它如果在第一次调用时已经被创建则直接使用。
4 图3.2 基于IIS 5.0进程模型的
ASP.NET运行时环境 IIS 6.0进程模型 如果Web服务器的操作系统是Windows Server 2003或更高版本IIS 6.0进程模型是
ASP.NET的默认选择。
该进程模型的名称已明确指出该模型需要IIS 6.0。
然而在Windows Server 2003计算机上仍可以使
ASP.NET按IIS 5.0的方式工作。
如果要这样做可以通过更改machine.config的lt processModel gt区段显式地启用该模型 ltprocessModel enablequottruequotgt 注意我们不提倡切换到过去的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内建的工作进程便会被禁用。
5 提示只有
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。
图3.3展示了IIS 6.0处理
ASP.NET应用程序和其他Web应用程序的过程。
【93】 图3.3 IIS 6.0中的
ASP.NET应用程序和其他Web应用程序的处理过程 IIS 6.0以内核级模块的形式实现了其HTTP监听程序。
因此所有的输入incoming请求会首先被一个驱动程序http.sys管理。
第三方代码不能与该监听程序进行交互。
这样即使用户模式出现问题IIS的稳定性也不会受到影响。
http.sys驱动程序会监听请求并将其追加到相应的应用程序池请求队列中。
一个叫“Web管理服务”Web Administration ServiceWAS的模块会读取IIS元库并指示http.sys驱动程序创建请求队列队列数量与元库中注册的应用程序池的数量一致。
【94】 总之使用IIS 6.0进程模型
ASP.NET会运行得更快因为inetinfo.exeIIS 管理服务与工作进程之间不需要进行任何进程间通信。
HTTP请求直接投递给承托CLR的工作进程。
此外
ASP.NET工作进程不是特殊的进程而仅仅是IIS工作进程的副本。
这样回收进程、缓存页面和监视运行状况的负担会由IIS承受。
6 在IIS 6.0进程模型中
ASP.NET会忽略machine.config文件 ltprocessModelgt区段中的大部分内容而只会从中读取线程和死锁设置。
任何从元库读取的数据只能够使用“IIS管理器”进行配置但其他配置信息仍会从.config文件中读取。
被请求页面的表示 每个引用.aspx资源的输入请求都会被映射到Page的派生类。
ASP.NET HTTP运行时环境首先会确定处理该请求的类名。
页面的URL与类名会通过某种命名约定关联在一起。
例如若请求的页面为default.aspx则可推断出相关联的类名为
ASP.default_aspx。
如果当前加载到AppDomain的程序集中不包含带有这个名称的类HTTP运行库将发出该类的创建和编译命令。
该.aspx资源的源代码会被解析以便创建该类的源代码结果会临时保存在
ASP.NET临时文件夹中。
接下来该类被编译并加载到内存中以便处理该请求。
当同一页面的请求再次到达时由于该类已经存在所以不会再次执行编译过程只有当.aspx源代码被更改后该类才会被重新创建并再次编译。
ASP.default_aspx类继承于Page类也可能继承于一个Page类的派生类。
更确切地讲
ASP.default_aspx的基类会与代码隐藏类合并——一个部分类由Visual Studio创建另一个部分类会由
ASP.NET HTTP运行库动态地组织。
图3.4以演示了该动态页面类源代码的生成过程。
【95】 部分类是新一代.NET编译器2.0和更高版本的特色功能之一。
在进行部分声明时一个类可以将其源代码拆分到多个源文件中每个源文件似乎都从头到尾包含普通类的定义。
新的partialC关键字会提示编译器当前处理的类不完整。
为获得完整的源代码编译器必须在命令行指定的文件中查找。
ASP.NET项目中的部分类 部分类非常适合于团队开发它简化了编码工作避免了使用用户定义的和工具生成的代码合并而产生的手动文件同步问题。
想要一个示例吗那么先看一下用Visual Studio 2003开发的
ASP.NET项目。
部分类是由编译器支持的功能为克服Visual Studio 2003项目包括
ASP.NET项目中工具生成的代码的脆弱性而专门设计。
部分类的合理使用消除了Visual Studio 2003为支持页面设计器而插入的使人困惑的、自动生成的、半隐藏式的代码区域。
一般而言部分类是一种源代码级的、程序集内部的、非面向对象的类行为扩展方式。
部分类的推行引出了许多好处。
例如我们可以使多个团队同时编写同一个组件。
此外对于向类中添加功能我们还获得了一种简明而优雅的方式。
ASP.NET运行库也应用了这项技术。
【9697】 7 ASPX标记定义的服务器控件会由代码隐藏类的程序处理。
为启用该模型代码隐藏类需要将这些服务器控件的引用以内部成员一般是受保护成员的形式进行整合。
在Visual Studio 2003中这些声明在我们保存标记时由集成开发环境IDE添加并存储在半隐藏区域中。
而在Visual Studio 2005中代码隐藏类是部分类只缺少成员的声明。
在运行时缺少的声明会由另一个
ASP.NET HTTP运行库创建的部分类补偿。
随后选择的编译器C、Visual Basic .NET等会合并这两个部分类以便创建动态生成页面类真正的父类。
图3.4
ASP.NET为一个将服务于请求的动态类生成代码的过程 提示 在Visual Studio 2008和.NET Framework 3.5中对于向现有.NET类中添加新功能除了部分类还有“扩展方法”extension method。
例如通过创建带有扩展方法的类我们可以将返回整数的ToInt32方法添加到System.String类中如果字符串的内容可以被转换为整数的话。
一旦将这个带有扩展方法的类添加到项目中项目中的所有字符串便获得了新方法。
智能感知完全支持这个功能。
3.1.2 请求的处理 为处理default.aspx页面
ASP.NET运行库需要获得
ASP.default_aspx类的引用。
如前所述如果当前加载到AppDomain的程序集中没有该类便创建它。
随后HTTP运行时环境通过公共接口IHttpHandler来调用该类。
根类Page实现了该接口它包含两个成员ProcessRequest方法和Boolean类型的IsReusable属性。
一旦HTTP运行库获得代表被请求资源的类的实例便会调用ProcessRequ8 est公共方法开始进行处理以便向浏览器做出响应后终结。
如前所述调用并执行ProcessRequest及其所触发事件的整个过程统称为“页面的生命周期”。
虽然
ASP.NET运行库是为页面服务的但它生成标记代码的方式比其他平台更复杂引入了许多对象。
ASP.NET工作线程无论是w3wp.exe还是aspnet_wp.exe会将任何输入的请求交给HTTP管道。
HTTP管道是一条完全可扩展的托管对象链其工作方式与一般意义上的“管道”颇为相似。
所有这些对象构成了所谓的
ASP.NET HTTP运行时环境。
【97】 HttpRuntime对象 页面请求会传递给管道中的每一个处理原始HTTP有效负载的对象在该链路的终端生成要发给浏览器的标记代码。
HttpRuntime类就是该管道的入口点。
对于输入的每个请求
ASP.NET工作线程通过创建HttpRuntime类的实例并调用其ProcessRequest来激活该HTTP管道。
要澄清一点尽管HttpRuntime.ProcessRequest与IHttpHandler接口的名称意思相近但二者实际并无关系。
HttpRuntime类包含许多私有和内部方法但只公开了三个静态方法Close、ProcessRequest和UnloadAppDomain详见表3.2。
表3.2 HttpRuntime类的公共方法 方 法 说 明 Close 从
ASP.NET缓存中移除所有项并终结当前Web应用程序。
该方法仅在自己代码中实现的宿主环境时使用。
在常规的
ASP.NET请求处理过程中不需要调用该方法 ProcessRequest 使所有
ASP.NET Web处理开始执行 UnloadAppDomain 终止当前应用程序。
应用程序会在下次接收到请求时重新启动 值得一提的是表3.2列出的方法在用户应用程序中的应用是有限的。
具体来讲开发者不应在自己的代码中调用ProcessRequest而Close也仅适用于使用自定义的应用程序来托管
ASP.NET时。
表3.2列出的3个方法中仅有UnloadAppDomain可以考虑在特定的运行时条件下使用实现对应用程序的重启详见本章后面的补充材料“哪些情况会导致应用程序重启”。
HttpRuntime对象会在创建时对许多辅助处理页面请求的内部对象进行初始化。
这些辅助对象包括缓存管理器和文件系统监视器用于检测构成应用程序的文件的变动。
ProcessRequest方法被调用后HttpRuntime对象即开始处理要发送到浏览器的页面。
它会为请求创建一个新的上下文空的并初始化一个特殊的文本编写器writer对象该对象用于缓存标记代码。
上下文对象是HttpContext类的实例它封装了所有与请求有关的HTTP特有的信息。
9 之后这个HttpRuntime对象使用上下文信息查找或创建能够处理该请求的Web应用程序对象。
通过包含在URL中的虚拟目录信息便可定位Web应用程序。
查找或新建应用程序的对象叫HttpApplicationFactory——它是一个内部使用的对象负责返回能够处理该请求的有效对象。
【9899】 在探究HTTP管道的其他组件之前我们先来看一下图3.5。
图3.5 页面的HTTP管道处理过程 应用程序工厂 在应用程序的生存期中HttpApplicationFactory对象维护着许多HttpApplication对象该对象用于处理输入的HTTP请求。
当该程序工厂对象被调用后它会验证请求的目标虚拟文件夹是否存在。
如果应用程序已运行该工厂则从可用的对象池中获取一个HttpApplication对象然后将它传给请求。
如果没有可用的则创建HttpApplication新 实例。
【99】 如果该虚拟目录不曾被调用则在新的AppDomain中针对该虚拟目录创建一个HttpApplication对象。
这样如果应用程序文件global.asax存在HttpApplication对象就需要对它进行编译并创建代表实际被请求页面的程序集。
该过程相当于启动应用程序。
HttpApplication对象用于处理页面请求每次处理一个多个对象用于处理并发的请求。
HttpApplication对象 HttpApplication是一个基类代表运行中的
ASP.NET应用程序。
运行中的
ASP.NET应用程序由动态创建的继承于HttpApplication的类来表示。
如果global.asax存在那么通过解析其内容可以创建动态生成的应用程序类的源代码。
如果global.asax可用应用程序类便会被创建并根据它命名为
ASP.global_asax。
否则会使用基类HttpApplication。
HttpApplication派生类的实例负责管理分配给它的请求的整个生命周期。
只有在该请求处理完毕后该实例才会被重用。
HttpApplication维护着一系列HTTP模块对象这些对象可以对请求的内容进行筛选甚至还可以进行修改。
在请求穿越管道的过程中可能随时会调用已注册的模块。
HttpApplication对象能判断代表被请求资源的对象类型一般可能是
ASP.NET页面、Web服务或用户控件。
随后HttpApplication使用相应的处理程序工厂获取代表被请求资源的对象。
工厂可能使用现有的程序集实例化被请求资源的类的实例也可能先动态创建所需程序集然后再实例化该对象。
处理程序工厂对象是实现IHttpHandlerFactory接口的类的实例负责返回处理HTTP请求的托10 管对象——HTTP处理程序。
一个
ASP.NET页面只是一个处理程序对象即实现IHttpHandler接口的类的实例。
页面工厂 HttpApplication类会确定要处理的请求的对象类型并委托与该类型相关的处理程序工厂创建其新的实例。
如果被请求的资源是页面会发生什么情况呢 一旦HttpApplication对象掌管了请求就必须选择一个合适的处理程序并创建该处理程序的实例。
对于面向页面的请求对应的工厂名为PageHandlerFactory。
为找到合适的处理程序HttpApplication会读取配置文件lthttpHandlersgt区段中的信息。
表3.3包含了几个主要的已注册处理程序。
【100101】 表3.3 .NET Framework中的处理程序工厂类 处理程序工厂 类 型 说 明 HttpRemotingHandlerFactory .rem .soap 用于实例化负责通过IIS传递的远程请求的对象和HttpRemotingHandler类型的对象 PageHandlerFactory .aspx 用于编译并实例化代表页面的类型。
在解析.aspx文件源代码时会建立该类的源代码。
它会实例化派生于Page类型的对象 SimpleHandlerFactory .ashx 用于实例化.ashx文件中源代码定义的HTTP处理程序。
它会实例化实现IHttpHandler接口的对象 WebServiceHandlerFactory .asmx 用户对Web服务源代码进行编译并将SOAP有效负载payload转换为方法调用。
它会实例化Web服务文件指定类型的对象 记住处理程序工厂不会在每次调用被请求资源时都进行编译操作。
已编译代码被存储在Web服务器的
ASP.NET临时目录中只要相应资源文件没有被更改便会被一直重用下去这种效果得益于对“工厂模式”的使用。
因此接到请求时页面处理工厂会创建代表被请求页面的对象实例。
如前所述页面对象继承于System.Web.UI.Page类而该类实现了IHttpHandler接口。
页面对象会被返回应用程序工厂随后被传回给HttpRuntime对象。
最后的步骤由
ASP.NET运行库完成
ASP.NET运行库会调用IHttpHandler的页面对象的ProcessRequest方法。
这会使页面执行用户定义的代码并为浏览器生成标记。
第14章将再次介绍
ASP.NET应用程序初始化、global.asax的内容以及HTTP上下文一种容器对象由HttpRuntime类创建沿管道传递最后被绑定到页面处理程序上中的信息。
.
上一篇:
ASP动态网站如何通过自定义连接字符串与Access数据库建立连接
下一篇:
2010年审计局工作总体计划