位置:海鸟网 > IT > ASP.NET >

从底层角度看ASP.NET(译文)

从底层角度看,通过ISAPI。看看这些后面发生了什么,让我们停止对asp.net的黑箱猜想。
ASP.NET是一个非常强大用来创建web应用程序的平台,它为创建web应用程序提供了大量的灵活强大的支持。大多数人仅仅熟悉表层的WebForm和webservice,他们位于整个ASP.NET架构的最表层。在这篇文章里,我将会描述非常底层的ASP.NET并且解释一个web请求是如何从web服务器到达ASP.NET运行时,并且通过ASP.NET管道(pipeline)处理请求的。
对我来说理解一个平台的内部机制,能够然我自己得到相应的满意和舒适,同时也可以帮助我们写出更好的应用程序。了解这些工具是怎样作为整个框架的某一部分而互相配合,并且更加容易的找到问题的解决方案,更加重要的,在发生错误的时候,能够帮助你定位以及调试这个错误。这篇文章的目标就是从系统的角度来看ASP.NET,并且帮助理解,请求是如何到达ASP.NET处理管道的。就这点而言,我们将会看到核心引擎,以及一个web请求是如何终结的。很多东西都是你在日常的工作中不需要知道的,但是它有利于理解ASP.NET架构怎样路由到你经常编写的应用程序的高层代码的。
大多数使用ASP.NET的人都是对Wenforms和WebService熟悉。这些高层的实现能够简化创建以web为基础的应用程序,并且,ASP.NET是一个驱动引擎,提供了对web服务器的底层接口,也为你在你的程序中用到的典型的高层前端服务的路由机制提供接口。WebForm和WebService仅仅是两个在ASP.NET框架核心上构建的非常经久耐用的HttpHandlers。
然而,ASP.NET在低层提供了更多的灵活性。HTTP Runtime和请求管道提供了所有的相同创建WebForm和WebService的能力,它们实际上也是由.NET托管代码实现。而且,你如果决定自己定制、创建一个比WebForm稍低层的平台,所有的ASP.NET的低层的这些功能、机制,你也同样可以使用。
WebForm显然是创建大多数web应用程序的最容易的方式,但是你在创建自定义内容的handlers或者对于进、出内容需要特殊的处理,或者你需要为另外一个应用程序创建一个定制的应用程序接口,使用低层的handlers或者modules能够给你更好的性能以及对一个web请求的更好的控制。你也可以绕过WebForm和WebService的这些高层实现提供的这些功能、机制直接在底层进行操作。

什么是ASP.NET


让我们以一个简单的定义开始:什么事ASP.NET?我喜欢把ASP.NET定义如下:
ASP.NET是一个使用托管代码完成的,从前到后处理web请求的,久经考验的框架。它并不仅仅是WebForm和WebService…
ASP.NET是一个请求处理引擎,它通过它的内部的管道将一个请求传送到一个开发者的代码上。实际上这个引擎完全独立于HTTP或者web服务器。事实上,HTTP Runtime是一个在IIS或者其他任何服务器之外的,您的应用程序的宿主环境。举一个例子,您可以将ASP.NET runtime放到一个Windows窗口中(点击获得更多详情)
运行时为请求通过这个管道提供了一个复杂而又优雅的机制。有一系列的相关对象,大多数都是可以在请求的每一个层次,通过实现其子类或者实现事件接口来进行扩展。通过这个机制能够接触到非常低层的接口,例如缓存,权限验证等。你甚至能在接受请求的前后过滤内容,或者将满足特定要求的请求转到你的代码或者其他的URL地址。有很多不同的方法来完成相同的事情,而且所有的这些方法实现的都非常直接,这样,就可以灵活的根据性能以及开发难度来选择最好的方法.

整个的ASP.NET引擎都是托管代码完成的,并且,可以支持通过托管代码进行拓展
整个的ASP.NET引擎都是托管代码完成的,并且,可以支持通过托管代码进行拓展.这是一个对.NET框架是否能够开发出久经考验的、性能良好的框架的有力的证明。然而,给人印象最深刻的部分是ASP.NET的深思熟虑的架构,能够使得这个结构非常易用,提供了处理请求的任何一个部分的能力。
使用ASP.NET你能够完成  以前是ISAPI扩展和ISAPI筛选器领域的工作,虽然带有一些局限性,但是比ASP要好很多。ISAPI是一个非常底层的Win32形式的API,它仅有非常贫乏的接口,非常难创建经久耐用的应用程序。由于ISAPI非常的底层而且非常快速,它处在非托管开发层。这样ISAPI有些时候主要用做连接其他的应用程序平台的桥。这并不意味着ISAPI已经死了。事实上ASP.NET在微软的平台上,正是通过ISAPI的一个扩展和ASP.NET的运行时和IIS进行交互的。ISAPI提供了Web服务器的核心接口,并且ASP.NET使用非托管的ISAPI代码来向客户端接收,发送数据。ISAPI提供的数据,是通过一些通用的对象暴露出去的,像HttpRequest和HtttpReponse,他们通过托管代码对象,以一个非常好的,易接触的接口形式,对外暴露非托管代码的内容。
 
从浏览器到ASP.NET
让我们从一个典型的ASP.NET Web Request的生命周期的最初开始。一个请求,在浏览器里,在一个用户输入一个URL地址或者点击一个超链接,或者提交一个HTML表单(一个post类型的请求)。或者一个客户端的程序会调用ASP.NET的WebService,这个WebService也使用过ASP.NET进行服务的。在服务器端,IIS5或者6接收到请求。在最底层,ASP.NET通过一个ISAPI扩展和IIS进行交互。这样的一个请求通常会被路由到一个以aspx为扩展名的页面文件,但是如何处理这个请求,完全取决与HTTP handler的实现,这个handler为了处理指定的扩展名而创立起来。在IIS里,.aspx 被‘应用程序扩展’(也可以成为脚本映射) 映射到ASP.NET ISAPI dll - ASP.NET_isapi.dll.每一个触发ASP.NET的请求都是必须通过在ASP.NET_isapi.dll指明和注册的扩展名。
 
根据扩展名,ASP.NET将请求路由到相应的,负责响应请求的handler。举一个例子,asmx这是一个WebService的扩展名,它不会被路由到磁盘上面的一个页面文件,而是路由到一个WebService类里面。其他很多的映射已经被ASP.NET安装了,而且你也可以定义你自己的。所有的这些HttpHandlers都是在ASP.NET  ISAPI里面指出,从而在IIS里面被映射,或者在web.config文件里面设置,路由到指定的HTTP Handler的实现。每一个handler,是处理指定的扩展名的一个.NET类,这个类可以简单到一个HelloWorld程序,也可以非常复杂,像一个ASP.NET page类或者 WebService 的实现。现在,理解扩展名是这种映射机制的基础,这种机制是ASP.NET用来从IIS获得一个用户请求然后将其路由到指定的处理请求的handler。

ISAPI是第一个也是性能最高的定制web请求处理的切入点。
ISAPI 连接
ISAPI是一个非常低层的非托管的win32API。这个接口根据ISAPI定义规范,非常的简单,还有优化过的性能。他们非常的底层-处理指针,用函数指针来进行回调-它们为开发者和工具提供最底层的,最好性能的,来处理IIS的接口。由于ISAPI非常的底层,他并适合创建应用程序级别的代码,而且,ISAPI趋向主要被用作 为高层工具提供应用程序服务器功能的 桥接口。举个例子,ASP和ASP.NET都是建立在ISAPI上面,还有Cold Fusion,运行在IIS上面的,大多数的Perl,PHP以及JSP的实现还有很多的第三方的解决方案,比如我的Web Connection framework for Visual FoxPro都是建立在ISAPI上面的。ISAPI是一个为高层应用程序提供接口的非常优秀的工具,这些接口抽象了ISAPI提供的信息。在ASP.NET和ASP中,这些引擎将ISAPI提供的信息抽象为像Request和Response这样的对象,使他们读取到ISAPI的Request信息。把ISAPI想象成铅锤。对于ASP.NET来说,ISAPI dll非常瘦小,仅仅是作为一个路由机制,以管道形式传送请求到ASP.NET运行时,所有的重型的处理甚至请求的线程管理,都在ASP.NET引擎和你的代码中。
 
依照协议,ISAPI支持ISAPI扩展和ISAPI筛选器。扩展是一个请求处理接口,并且提供处理web服务器的传入传出的逻辑,它本质上是一个事务接口。ASP.NET和ASP都是做为ISAPI扩展被实现的。ISAPI过滤器是一组接口,他们能 查看每一个进入IIS的请求,修改内容,或者改变类似于验证功能的行为。顺便提一句,在ASP.NET中,通过两个概念映射了类似ISAPI的功能:HTTPHandlers(扩展)和HttpModules(筛选器)。一会,我们看详细的内容。
 
ISAPI是标记着ASP.NET的请求的初始代码。ASP.NET映射了各种扩展名到ISAPI的扩展里,这些映射都在.NET Framework的目录下:
<.NET FrameworkDir>\ASP.NET_isapi.dll
你可以交互式的在IIS服务管理器里面看到这些映射,如图一.选择你的网站,然后“主目录”,“配置”,“映射”。

 

图 7 – ASP.NET请求管道流程通过一系列事件接口,提供了很大的灵活性。Application扮演了一个宿主容器的角色,它加载了Web应用程序,并且随着请求的进入和在管道中的传递而触发事件。每一个请求都是通过相同的路径,通过HTTP Filters和设置了的Modules。Filters能够检测通过管道的每一个请求,而Handlers允许实现用用程序的逻辑和接口,就像WebForm和WebService。为了提供应用程序的输入和输出Context在整个过程提供了请求的相关信息。
 
WebForm在这个基础框架上面,通过实现一个HTTPHandler以及更高层的接口,然而最终,Wenforms的Render()方法简单的使用一个HtmlTextWriter对象将其最终的输出结果写入到context.Response.OutputStream。这样,最终,一个高层的工具,像WebForms仅仅是一个Request和Response对象的高层的抽象。
 
你可能想知道,这点上,你是否需要处理HTTPHandler。毕竟,WebForm提供了简单的可访问的HTTPHandler实现,那么我们为什么要放弃这个灵活性而不厌其烦的做一些底层的事情呢?
 
WebForm对于生成复杂的HTML页面和需要图形布局工具,模板化页面的商业逻辑非常的好。但是,WebForm执行了很多的增加消耗的任务。如果你仅仅想在系统中读取一个文件,并将其返回,那么你可以跳过Web Form page框架,直接处理文件。如果你做的事情像从数据库提供图片,你也不需要Page框架—你不需要模板,而且没有一个UI。没有理由创建一个页面对象和Seesion并且处理页面级别的事件.

所以handlers更加高效。Handler也可以完成WebForm不能完成的任务。例如,他能够处理一个请求,不需要磁盘上有物理文件。 做这个,你需要在图1中的应用程序扩展对话框中。
关闭“检查文件是否存在”选项。
 
对于内容提供者是通用的,就像动态图片处理,XML服务器,Url重定向提供构造的Url,下载管理等等,这些都不是适合Wenform引擎。
对你来说,我介绍的足够了么?
恩,我们这里已经介绍了处理整个请求的过程。有很多的底层信息,我没有仔细的讲HTTPHandler和HTTPModule具体工作细节。挖掘这些信息需要一些时间,在理解ASP.NET怎样工作上面,希望能给你和我自己一样的满意程度。
 
在结束之前,让我们简短的回顾一下从IIS到handler的事件序列:
•IIS获得请求
•检测脚本映射,映射到ASP.NET_isapi.dll
•触发工作进程(ASP.NET_wp.exe 在 IIS5 或者 w3wp.exe 在 IIS6)
•.NET运行时加载
•IsapiRuntime.ProcessRequest()通过非托管代码调用
•IsapiWorkerRequest created once per request
•IsapiWorkerRequest 每一次请求创建一次
•HttpRuntime.ProcessRequest() called with Worker Request
•通过传进Work Request, HttpContext对象被创建
•HttpApplication.GetApplicationInstance() called with Context to retrieve instance from pool
•HttpApplication.Init() 调用,并且启动管道事件序列,附加Modules和Handler
•被调用,开始处理进请求
•管道事件触发
•Handlers被调用,并且ProcessRequest 方法执行
•控件返回管道并且发送请求事件触发
 
通过这个简单的列表,把这些是如何组合起来的记住会更容易。我不时的来看它来记忆。现在,我们回到工作上,继续做一些不抽象的…
尽管,这里我说的是基于ASP.NET1.1,但是ASP.NET2.0中,并没有改变这些底层的处理过程。
非常感谢微软的Mike Volodarsky来审阅这篇文章,并且提了一些附件的提示并且 Michele Leroux Bustamante提供了ASP.NET管道请求基础信息。