[转]ASP.NET页面解析

可以看到,我先前发的很多文章都是关于asp.net的原理的文章,,之所以这么做,是因为我觉得如果你想自由驾驭上层的东西,,底层的东西你必须了解,当然掌握的话会更好。


 

 

 在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——它是一个内部使用的对象,负责返回能够处理该请求的有效对象。

 

 

 

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请求的托管对象——HTTP处理程序。一个ASP.NET页面只是一个处理程序对象(即,实现IHttpHandler接口的类的实例)。

页面工厂

HttpApplication类会确定要处理的请求的对象类型,并委托与该类型相关的处理程序工厂创建其新的实例。如果被请求的资源是页面,会发生什么情况呢? 一旦HttpApplication对象掌管了请求,就必须选择一个合适的处理程序,并创建该处理程序的实例。对于面向页面的请求,对应的工厂名为PageHandlerFactory。为找到合适的处理程序,HttpApplication会读取配置文件<httpHandlers>区段中的信息。

几个主要的已注册处理程序:

 

处理程序工厂不会在每次调用被请求资源时都进行编译操作。已编译代码被存储在Web服务器的ASP.NET临时目录中,只要相应资源文件没有被更改,便会被一直重用下去(这种效果得益于对“工厂模式”的使用)。 因此,接到请求时,页面处理工厂会创建代表被请求页面的对象实例。如前所述,页面对象继承于System.Web.UI.Page类,而该类实现了IHttpHandler接口。页面对象会被返回应用程序工厂,随后被传回给HttpRuntime对象。最后的步骤由ASP.NET运行库完成,ASP.NET运行库会调用IHttpHandler的页面对象的ProcessRequest方法。这会使页面执行用户定义的代码,并为浏览器生成标记。

页面的处理指令

页面指令的处理用于配置执行该页面的运行时环境。在ASP.NET中,指令可以位于页面的任何位置,但良好且常见的习惯是将其置于文件的开始部分。除此,页面指令的名称是不区分大小写的,且指令的属性(attribute)值也不必加引号。@Page是ASP.NET中最重要的也是最常用的指令。列出了所有ASP.NET指令。

除@Page、@PreviousPageType、@Master、@MasterType和@Control外,所有指令都可以在页面和控件中声明。@Page和@Control是互斥的:@Page仅能用在.aspx文件中,而@Control指令仅能用在.ascx文件中。@Master用于定义一种非常特殊的页面——母版页(master page)。 处理指令的语法较特殊,但对于所有指令类型都是一致的。多个属性(attribute)必须用空白分割,属性与值间的等号(=)两边不能有空白,如下所示:

<%@ Directive_Name attribute="value" [attribute="value" … ] %> 

每种指令都有自己类型化的属性集合。将错误类型的值赋给一个属性,或在某个属性中使用错误的属性,都会导致编译错误。

要点:指令的属性内容总是以纯文本的形式被解析。然而,对于某些属性,应包含能够被解析为特定.NET Framework类型的值。当ASP.NET页面被解析时,所有指令属性 会被提取,并存储在一个字典中。属性的名称和数目必须与该指令所对应模式相匹配。只要用字符串表示的属性值能够被转换为目标类型,那么它便是有效的。例如,如果某个属性只接受布尔(Boolean)类型的值,则只有true和false是有效的。

Page类

在.NET Framework中,Page类提供了ASP.NET应用程序从.aspx文件创建的所有对象的基本行为。Page类在System.Web.UI命名空间中进行了定义,该类派生自TemplateControl并实现了IHttpHandler接口:

public class Page : TemplateControl, IhttpHandler

具体说来,TemplateControl是抽象类,为ASP.NET页面和用户控件提供了一组基本的功能。在继承层次的上一层,我们会发现Control类。它定义了由ASP.NET服务器端元素(页面、控件和用户控件)共享的属性、方法和事件。 从一个实现INamingContainer的类(TemplateControl)派生,Page还充当其所有组成控件的“命名容器”。在.NET Framework中,控件的命名容器是实现INamingContainer接口的第一父控件。对于任何一个实现命名容器接口的类,ASP.NET会创建一个新的虚拟命名空间,以便确保控件树中的所有子控件有唯一的名称(对于GataGrid之类的迭代式数据绑定控件和用户控件,这是一个非常重要的特性)。 Page类还实现了IHttpHandler接口的方法,从而可以充当一种特定类型(针对.aspx文件)的HTTP请求处理程序。IHttpHandler接口的关键成员为ProcessRequest方法,ASP.NET会调用该方法发起对页面的处理,真正地对请求进行处理。

提示:INamingContainer是一个没有方法的标志性接口。然而仅仅因为它的存在,才使得ASP.NET运行库创建额外的命名空间,对实现它的页面(或控件)的子控件进行命名。Page类是所有页面控件的命名容器,当然,本身实现INamingContainer接口的控件或实现该接口的子控件除外。

内部对象

  

 

 

事件模型

当某个页面被请求时,它的类及其所包含的服务器控件会负责处理请求,呈现HTML输出,并随后发回客户端。由于HTTP协议的固有特性,客户端与服务器间的通信是无状态且非连接的。而实际的应用程序需要状态,以便维护对同一页面的后续调用。使用ASP或其他服务器端开发平台(如Java Server Page和LAMP一类的基于Linux的系统),开发者必须负责状态的存储。而ASP.NET提供了一种内建架构,能够以透明的方式对页面的状态进行存储和恢复。尽管基于无状态的协议,但以这种方式,从客户端体验到的是连续的执行过程。然而,那只是一种表象。

视图状态简介

连续性所导致的这种表象,一方面与页面的设计和工作方式有关,另一方面是ASP.NET页面视图状态造成的。与此同时,服务器端控件也发挥着重要作用。简而言之,在页面将其内容呈现为HTML之前,页面要将自身及其所包含的控件的状态信息存储在持久性介质(一般为隐含字段)中。当该页面回发后,其状态会从隐含字段中被反序列化,用于对声明在页面布局中的服务器控件实例进行初始化。 每个页面实例有其特有的视图状态,因为该信息嵌入在HTML中。这样做的好处是,控件会以上一次创建的视图状态(即该页最后一次被呈现发送到客户端时的状态)的值进行初始化。此外,页面周期中还会有一个阶段,将已存储的状态与由客户端做出的更新合并。在回发后,页面执行时,它会发现一个有状态的且更新过的上下文,就像工作在连续的点对点连接上一样。 这里做了两个假设。第一个假设是,页面总是投递给自身,并携带着状态信息。第二个假设是,服务器端控件必须带有runat=server属性,以便在页面回发后具有“生命力”。

单窗体模型

不可否认,对于具有ASP或JSP经验的程序员来说,开始可能不太适应ASP.NET的单窗体模型。这些程序员在论坛和新闻组经常会问这样的问题:“窗体的Action属性在哪里?”以及“为什么我提交窗体时,不能重定向到一个特定页面?” ASP.NET页面只支持一个服务器端<form>(窗体)标签。所有要与服务器交互的控件,必须全部置于在该窗体中。窗体和控件都必须带有runat属性,否则会被视为纯文本,并被逐字输出。在服务器端,窗体是HtmlForm类的实例。HtmlForm类没有暴露任何相当于HTML <form>标签的Action的属性。其原因在于,ASP.NET页面总是投递给自己。除Action属性外,窗体其他常用属性(如Method和Target)还是完全支持的。 不包含服务器端窗体的,以及使用HTML窗体(不带runat属性的<form>标签)的页面,也是有效的ASP.NET页面。在ASP.NET页面中,HTML和服务器窗体可以同时存在。然而只能有一个<form>标签的runat属性设置为server。HTML窗体会像一般情况一样,使我们能够向程序中的任何页面投递。但这样的做问题在于,状态信息不会被自动存储。换言之,仅当窗体使用一个服务器<form>元素时,ASP.NET Web窗体模型才会工作.

异步页面

ASP.NET页面会被HTTP处理程序作为Page类的实例处理。每个请求会占用ASP.NET线程池中的一个线程,在请求完毕后该线程才会被释放。倘若被请求的页面频繁地启动外部的、高耗时的任务呢?问题在于,ASP.NET进程虽然闲置,但池中没有空闲的线程来处理新入的其他页面的请求。这多应归因于HTTP处理程序(包括页面类)的同步工作方式。为减轻这个问题,自1.0版开始,ASP.NET支持了异步处理程序(通过IHTTPAsyncHandler接口)。而从ASP.NET 2.0版开始,由于有了框架的支持,创建异步页面变得更加容易了。 异步ASP.NET页面的构建涉及两个方面:@Page指令的一个新属性以及注册的若干异步执行的任务。异步任务可以通过两种途径注册。可以为PreRenderComplete事件定义异步处理程序Begin/End对儿,也可以创建代表异步任务的PageAsyncTask对象。这在PreRender事件被引发之前进行就可以,但一般是在Page_Load事件中。


 源 http://www.cnblogs.com/gaoweipeng 

 

转载于:https://www.cnblogs.com/suncms/archive/2012/05/15/2501986.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值