.NET处理HTTP请求
.NET处理HTTP请求
.NET平台处理HTTP请求的过程大致如下:
1 IIS得到一个请求;
2查询脚本映射扩展,然后把请求映射到aspnet_isapi.dll文件
3代码进入工作者进程(IIS5里是aspnet_wp.exe;IIS6里是w3wp.exe),工作者进程也叫辅助进程;
4.NET运行时被加载;
5非托管代码调用IsapiRuntime.ProcessRequest()方法;
6每一个请求调用一个IsapiWorkerRequest;
7使用WorkerRequest调用HttpRuntime.ProcessRequest()方法;
8通过传递进来的WorkerRequest创建一个HttpContext对象
9通过把上下文对象作为参数传递给HttpApplication.GetApplicationInstance(),然后调用该方法,从应用程序池中获取一个HttpApplication实例;
10调用HttpApplication.Init(),启动管道事件序列,钩住模块和处理器;
11调用HttpApplicaton.ProcessRequest,开始处理请求;
12触发管道事件;
13调用HTTP处理器和ProcessRequest方法;
14把返回的数据输出到管道,触发处理请求后的事件。
当 客户端向Web服务器请求一个页面文件时,这个HTTP请求会被inetinfo.exe进程截获(WWW服务),它判断文件后缀,如果 是*.aspx、*.asmx等,就把这个请求转交给aspnet_isapi.dll,而aspnet_isapi.dll则会通过一个Http PipeLine的管道,将这个HTTP请求发送给w3wq.exe进程,当这个HTTP请求进入w3wq.exe进程之后,Asp.Net framework就会通过HttpRuntime来处理这个HTTP请求,处理完毕后将结果返回给客户端。
当 一个HTTP请求被送入到HttpRuntime之后,这个HTTP请求通过HTTP管道(HttpRuntime是HTTP管道的入口)被送入到一个被 称之为HttpApplication Factory的一个容器当中,而这个容器会给出一个HttpApplication实例来处理传递进来的HTTP请求,同时 HttpApplication实例会创建一个HttpContext对象来记录HTTP请求的上下文,而后这个HTTP请求会依次进入到如下几个容器 中:
HttpModule --> HttpHandler Factory --> HttpHandler
当系统内部的HttpHandler的ProcessRequest方法处理完毕之后,整个Http Request就被处理完成了。
如果想在中途截获一个httpRequest并做些自己的处理,就应该在HttpRuntime运行时内部来做到这一点,确切的说时在HttpModule这个容器中做到这个的。
过程详解:
从 本质上讲,Asp.Net主要是由一系列的类组成,这些类的主要目的就是将Http请求转变为对客户端的响应。HttpRuntime类是Asp.Net 的一个主要入口,它有一个ProcessRequest方法,这个方法以一个 HttpWorkerRequest 类作为参数。HttpRuntime类几乎包含着关于单个Http请求的所有信息:所请求的文件、服务器端变量、QueryString、Http头信息 等等。Asp.Net 使用这些信息来加载、运行正确的文件,并且将这个请求转换到输出流中,一般来说,就是HTML页面(二般来说,也可以是张图片)。
对 于IIS来说,它依赖于一个叫做HTTP.SYS的内置驱动程序来监听来自外部的HTTP请求。在操作系统启动的时候,IIS首先在HTTP.SYS中注 册自己的虚拟路径(实际上相当于告诉HTTP.SYS哪些URL是可以访问的,哪些是不可以访问的。举个简单的例子:为什么你访问不存在的文件会出现 404 错误呢?就是在这一步确定的)。
服务器处理一个.htm页面和一个.aspx页面肯定是不一样 的,那IIS依据什么去处理呢?――根据文件的后缀名。能够处理各种后缀名的应用程序,通常被称为 ISAPI 应用程序(Internet Server Application Program Interface互联网服务器应用程序接口),它的主要工作是映射所请求的页面(文件) 和与此后缀名相对应的实际的处理程序。
所有的.aspx文件实际上都是由aspnet_isapi.dll这个程序来处理的,当IIS把对于.aspx页面的请求提交给了aspnet_isapi.dll以后,它就不再关心这个请求随后是如何处理的了。
除了映射文件与其对应的处理程序以外,ISAPI还需要做一些其他的工作: 1. 从HTTP.SYS中获取当前的Http请求信息,并且将这些信息保存到
HttpWorkerRequest 类中。
2. 在相互隔离的应用程序域AppDomain中加载HttpRuntime。
3. 调用HttpRuntimeProcessRequest方法。
接下来才是程序员通常编写的代码所完成的工作了,然后,IIS接收返回的数据流,并重新返还给 HTTP.SYS,最后,HTTP.SYS 再将这些数据返回给客户端浏览器。
名词解释:
IIS: Internet Information Server是一种基于Windows平台的网页服务(World Wide Web server)组件,其中包括WWW服务器、FTP服务器、NNTP服务器和SMTP服务器,分别用于网页浏览、发布信息、文件传输、新闻服务和邮件发送 等方面。
IIS的4个核心组件:
HTTP.sys:将 HTTP 请求传送到用户模式应用程序的内核模式设备驱动程序。
WWW服务管理和监视组件:配置“万维网发布服务“(WWW 服务)并管理工作进程。
工作进程:处理提交到分配给它们的 Web 应用程序的请求。
Inetinfo.exe:主控配置数据库和非 Web 服务。
MSIL:Microsoft Intermediate Language,微软中间语言。MSIL是将.Net代码转化为机器语言的一个中间过程,它是一种介于高级语言和基于Intel的汇编语言的伪汇编语 言。同一段MSIL代码可以被不同的编译器实时编译并运行在不同的结构上,因此MSIL本身与机器无关,可以在装有CLR的任一计算机上运行。
CLR:Common Language Runtime,公共语言运行时。简单的理解,就是一个翻译,把.NET平台的各种语言(C#、VB、JS等)翻译为机器可以执行的语言。.NET编译器 将程序代码编译成MSIL(Microsoft Intermediate Language ,微软中间语言),然后再由CLR中的JIT(Just In Time,实时)编译器去编译成机器语言来执行。CLR在功能上就如同一块虚拟的CPU,它执行MSIL代码、操作数据。CLR和真实的CPU类似之处在 于它们都不直接操作内存中的变量而是使用程序变量的临时拷贝,CLR把这些程序变量存放在堆栈上。从内存拷贝某个变量到堆栈的行为称作装载 (loading),而从堆栈拷回某个变量到内存的行为则被称作存储(storing)。
HttpRuntime: 配置 ASP.NET HTTP 运行时设置,这些设置确定如何处理对 ASP.NET 应用程序的请求,HttpRuntime类几乎包含了单个HTTP请求的所有信息,HttpRuntime是HTTP管道的入口。HTTP请求到达 时,ASP.NET 将运行时库加载到要处理请求的进程中,还为将在 Web 服务器上运行的每个 Web 应用程序创建一个应用程序域(AppDomain)。HttpRuntime的ProcessRequest 方法驱动所有后续的 ASP.NET Web 处理。
AppDomain:AppDomain 对象表示应用程序域,即一个应用程序执行的独立环境,为执行托管代码提供隔离、卸载和安全边界。它是是微软基于.NET框架设计的概念,找不到其他技术体 系中贴切的参照概念。辅助进程为每个当前正在运行的 ASP.NET应用程序维护一个特定的AppDomain。一个进程中可以有多个AppDomain,但是一个AppDomain只能存在于某个进程中。 线程执行可以涉及多个AppDomain,但某个特定时刻线程仅存在于一个AppDomain中,且线程可以进入其他的AppDomain。
哪些情境下要使用AppDomain:
1、需要隔离的程序集,譬如一些特别容易引起崩溃的代码可以考虑单独运行于一个特定的AppDomain。
2、不同安全级别的程序集,如果需要为自己的代码划分安全执行的边界,可以考虑将不同安全级别的代码单独创建于某个设定了不同安全信息的AppDomain。
3、 从性能上考虑,有些程序集可能会消耗大量资源,尽管在托管环境下,基本上不存在资源消耗漏洞,但是总会存在特定时间访问密集造成消耗大量资源的情况,这时 可以考虑创建单独的AppDomain,在资源消耗超过临界点后进行AppDomain的卸载,适应系统运行要求。ASP.NET中利用不同得 AppDomain来提供支持就是为了防止一个应用程序的崩溃影响其他ASP.NET应用程序,同时,在不重新启动的系统不重新启动IIS不影响 ASP.NET自身服务提供的情况下将一个AppDomain卸掉同时启动新的AppDomain,理想情况下可以实现web系统的长时间在线(这以往是 昂贵的UNIX的特性,终于被MS借鉴了)。
4、不同版本的同一应用程序集的同时运行。这个在COM时代是一个大问题,现在通过AppDomain,实现了在一个进程中执行版本不同的两个程序集,可以做到良好的兼容性。
5、动态加载一些程序。
HttpApplication: 定义了所有ASP.Net应用程序的通用的方法、属性和事件。是你实际可以看到的进入HTTP运行时(HttpRuntime)的第一个登录点,它对应到 Global.asax文件里定义的基类。HttpApplication担当主容器,负责加载Web程序,当请求到来时触发事件以及在管道之间传输请求 (事件消息的邮递者),然后通过传递HttpContext对象,把事件消息发送给调用的方法(在HttpModule和HttpHandler中)。每 一个请求都将被路由到一个HttpApplication对象。HttpApplicationFactory类会为你的ASP.NET程序创建一个 HttpApplication对象池,它负责加载程序和给每一个到来的请求分发HttpApplication的引用。这个 HttpApplication对象池的大小可以通过machine.config里的ProcessModel节点中的 MaxWorkerThreads选项配置,默认值是20(此处可能有误,根据Reflector反编译的代码,池的大小应该是100)。 HttpApplication对象池开始启动时通常只有一个HttpApplication对象。但是当同时有多个请求需要处理时,池中的对象将会随之 增加。而HttpApplication对象池,也将会被监控,目的是保持池中对象的数目不超过设置的最大值。当请求的数量减小时,池中的数目就会跌回一 个较小的值。HttpApplication对象运行在AppDomain里。
HttpApplication的主要职责是作为HTTP管道的事件控制器,所以它的接口主要包含的是事件。事件挂接是非常广泛的,包括以下这些:
l BeginRequest
l AuthenticateRequest
l AuthorizeRequest
l ResolveRequestCache
l AquireRequestState
l PreRequestHandlerExecute
l …Handler Execution…
l PostRequestHandlerExecute
l ReleaseRequestState
l UpdateRequestCache
l EndRequest
HttpModule:实现了System.Web.IhttpModule接口的.NET组件。
这 些组件通过在某些事件中注册,把自己插入ASP.NET请求处理管道中(HttpApplication事件链)。当这些事件 (HttpApplication对象触发的事件)发生时,HttpModule就会去处理事件(一般是截获请求),所以HttpModule本质上就是 过滤器(当一个HTTP请求到达HttpModule时,整个ASP.NET Framework并未对这个HTTP请求做过任何处理)。HttpModule有访问HttpContext对象的权限(但某些对象可能还不能使用), 可以修改请求,输出响应的内容以及提供自定义的身份验证,另外还可以在特定的程序里,针对ASP.NET的每一个请求提供响应前处理和响应后处理。多个 HttpModule可以钩住相同的事件,事件被处理的顺序是它们在web.config里配置的顺序。
ASP.NET系统中默认的HttpModule
DefaultAuthenticationModule:确保上下文中存在Authentication 对象,无法继承此类。
FileAuthorizationModule:验证远程用户是否具有访问所请求文件的 NT 权限,无法继承此类。
FormsAuthenticationModule:启用ASP.NET应用程序以使用Forms身份验证,无法继承此类。
PassportAuthenticationModule:提供环绕PassportAuthentication服务的包装,无法继承此类。
SessionStateModule:为应用程序提供会话状态服务。
UrlAuthorizationModule:提供基于 URL 的授权服务以允许或拒绝对指定资源的访问,无法继承此类。
WindowsAuthenticationModule:启用 ASP.NET 应用程序以使用 Windows/IIS 身份验证,无法继承此类。
HttpHandler:HttpHandler(HTTP处理器)是HTTP请求的处理中心,它可以完全使用HttpContext对象,真正地对客户 端请求的页面进行编译和执行,并将处理后的信息附加在HTTP请求信息流中返回到HttpModule中。HttpHandler通过一个简单 IHttpHandler接口实现(或者它的异步版本IHttpAsyncHandler),此接口仅仅只有一个方法ProcessRequest()和 一个属性IsReusable。