ASP.NET WebForms底层请求处理机制初探
1 处理过程分析
对客户端的Http请求,WebForms底层将经历如下处理过程:请求由客户端传递到服务器端,IIS服务器将请求传递给ISAPI扩展,ISAPI扩展启动ASP.NET运行时环境,该环境生成HttpApplication实例,该实例调用对应的HttpHandler的ProcessRequest方法来处理请求,并将处理结果集合反馈客户端。具体分析如下:
1.1 从浏览器到ISAPI扩展
首先,当客户端发出Http请求(http://webserver/Library/Logon.aspx)后,IIS捕获到这个请求并进行分析。若发现其后缀名为.aspx,则映射到ISAPI扩展[3]。ASP.NET就是通过扩展ISAPI,和IIS建立关联的。.NET中扩展的ISAPI是<.NET FrameworkDir>中的aspnet_isapi.dll。
在对WebForms的请求处理过程中,IIS主要起到了提供映射的作用。它把接受的请求交给ISAPI扩展,由扩展负责转交ASP.NET运行时去处理。这时,控制权由IIS交到了ISAPI扩展。而那些关于ASP.NET的映射是由aspnet_regiis工具在安装ASP.NET环境时进行注册的。在IIS中的所有映射关系可以在IIS中项目Library属性 | 配置 | 应用程序映射中查看到,表1中列举了部分为ISAPI扩展的映射关系:
表1 部分应用程序映射
|
1.2 加载 ASP.NET运行时
第二步,ISAPI扩展会将请求排队,并通过命名管道[4]将请求分配给ASP.NET工作进程aspnet_wp。然后工作进程将请求交给一系列称为的 HTTP 管道的托管对象,这个时候,.NET运行时就已经自动载入了。当运行时启动,工作进程将根据所给的虚拟路径(“/Library”)决定创建一个新的应用程序域AppDomain对象或是使用先前已创建好的应用程序域来处理请求。若是创建一个新的应用程序域,还将加载本项目的私有程序集和具体请求的页面(logon.aspx)。
获取应用程序域后,aspnet_wp将请求转发至其中的ISAPIRuntime对象。ISAPIRuntime是一个COM接口的引用,在ISAPI扩展中定义,由aspnet_wp调用。由于它的一个未托管指针指向ISAPI的扩展控制块(ECB),所以它能获得请求的必要信息,是请求访问.NET运行时的入口点。
然后ISAPIRuntime对象将请求信息转交给HttpRuntime。HttpRuntime会创建一个HttpContext对象,并将取自ISAPIRuntime对象中的请求信息填入该对象。HttpContext对象因此生成了:Request,Response等对象。
具体处理过程如图1:
![]() |
第三步,就需要为请求建立会话。 建立了HttpContext对象后,HttpRuntime还要为每一个提出的请求用户创建HttpApplication对象,并初始化相应的管道事件,启动管道进程。HttpRuntime向HttpApplication工厂提出请求,要求返回一个HttpApplication对象。HttpApplication工厂在接收到请求后,会先检查是否有已经存在并空闲的HttpApplication对象。如果有,则从池中取出一个HttpApplication对象返回给HttpRuntime;如果没有的话,则要创建一个HttpApplication对象给HttpRunTime。
HttpApplication工厂创建HttpApplication对象的过程是一个转化与动态编译的过程,整个过程是这样:HttpRuntime根据请求检查路径”/Library/Logon.aspx”是否存在。若不存在通知IIS返回HTTP404错误,若存在则在ASP.NET的临时目录中查找相应的程序集文件。然后对Global.asax文件进行转化,和Library.dll中的Global类合并,创建出新的寄生类(即Global_asax类),最后编译此寄生类,取得对象返回给HttpRuntime对象,这个对象就是HttpApplication。asax文件的转换和编译只发生在此虚拟目录第一次处理用户请求时,或者是Global.asax文件变动时。生成HttpAplication过程如图2:
![]() |
然后,HttpApplication开始初始化相应的管道事件,这个过程就是加载相关的HttpModules的过程。HttpModules有Session Module,Cache Module等。
加载HttpModules完毕,HttpRuntime对象紧接着调用HttpApplication.ProcessRequest。此函数将执行权转交到对应的HttpHandler上。
1.4 HttpHandler处理
第四步,创建一个HttpHandler对象。它由全局mechine.config或web.config中的<httphandlers> 配置节指定[5]。在全局 machine.config 文件中,ASP.NET 将所有的对 WebForms的请求映射到 PageHandler工厂类。
然后PageHandler工厂开始请求的处理,即转换和编译aspx文件。由于此次请求的页面是Logon.aspx,所以将对Logon.aspx进行转换和编译。此过程和对Global.asax的处理类似,也是先将该页面转换成类代码,然后从Libray.dll的页面类中取出Logon类的代码,将二者混合编译一个新的寄生类Logon_aspx类,并实例化返回给HttpHandler对象。
最终处理客户端请求的是类Logon_aspx的实例。Logon_aspx类的声明为:
public class Logon_aspx : Logon, IRequiresSessionState
Logon_aspx类继承Logon类,即完成了aspx文件和aspx.cs文件的合并。同时Logon类继承于Page类,即实现了HttpHandler接口,这表明Logon_aspx类有处理页面请求的能力。
1.5 执行ProcessRequest
第五步,HttpApplication调用对应的HttpHandler的ProcessRequest方法来处理用户请求。HTTP 处理程序的大量工作就集中在实现处理程序的ProcessRequest方法中。
HttpHandler有IHttpHandler和IHttpAsyncHandler 两个接口,所有的页面请求工作都是通过IHttpAsyncHandler的BeginProcessRequest方法异步调用后台操作完成的。而同步的IHttpHandler接口,WebForms没有直接使用。
BeginProcessRequest方法执行时,将实例化Logon_aspx类,并首先调用FrameworkInitialize方法构建了页面的整个控件树。然后就开始执行整个页面的生命周期。
最后把产生HTML代码,传递给Response对象。ISAPIWorkRequest负责将输出流返回给ISAPI扩展的WriteClient方法,由IIS执行输出。这样,用户的客户端浏览器中就出现了一个简单的登录界面,客户端的Http请求到此完成。