【asp源码栏目提醒】:网学会员为广大网友收集整理了,ASP.NET Framework深度历险 - 编程语言,希望对大家有所帮助!
ASP.NET Framework 深度历险 作者:uestc95263.net 时间:2002/05/01 此 PDF 电子文档可以自由传播、拷贝、打印探讨技术问题可以联系我:Uestc95263.net MSN:Uestc95hotmail.com2 第四章
ASP.NET Framework 深度历险 (uestc95263.net) 写在前面的话 这篇电子文档本来是作为一本打算出版的技术书籍的其中一个章节撰写的,各位在阅读的时候会注意到这一点(带有章节编号),但是由于工作繁忙和其他的一些原因,这本技术书籍未能出版。
其实出版与否不重要,重要的是如果能对大家在学习
ASP.NET 的过程中有所帮助就足够了。
最初这份电子文档是在www.dev-club.com论坛上面陆续发表的,但是由于论坛的条件限制,无法将完整的文章展现在大家面前。
在长时间的忙碌工作之后,终于有时间将这篇电子文档整理出来配上插图以及 C代码,方便大家阅读。
如果这篇电子文档能对使得你对
ASP.NET 技术有更深层次的理解,我就会高兴不已了,:- 如果你想要索取相关的 C代码,可以发送电子邮件到:uestc95263.net ,并在邮件主题注明:“索取《
ASP.NET 深度历险》源代码”。
目前从事的工作的.NET Framework 技术架构和组件开发的研究,如果有志同道合的朋友,也希望我们能多多探讨。
第四章
ASP.NET Framework 深度历险 3 第四章
ASP.NET Framework深度历险 本章内容主要是深入的探讨有关
ASP.NET 架构
ASP.NET Framework方面的技术细节和核心机制。
ASP.NET 技术一种强大的技术,其相对于以往的
ASP 技术而言,它已经不是简单的进步可以形容的了,而是一场彻底的 Web 开发技术革命。
而我们只有透彻的了解才能真正的驾驭它。
如果不深入的了解一些
ASP.NET Framework 内部的一些机制,我们即便是口口声声说自己使用
ASP.NET 技术来构建 Web 应用程序其实也和以前的
ASP 没有什么两样的,一方面
ASP.NET 技术为了我们的快速开发而封装了所有的技术细节,但是另一方面,也间接的使我们成了简单的 Coding man,只是机械的去用
ASP.NET 提供出来的种种便利的控件或者事件接口。
如果想成为真正的
ASP.NET 开发高手, 透彻的深入理解
ASP.NET Framework 底层技术细节是不可避免的,也是必须要去做的,否则你只能成为浮于表面的 Coding man。
在这里本章内容不打算简单介绍
ASP.NET 的入门知识,我们也不会浪费时间去做这些。
ASP.NET 除了名字和古老的
ASP 有些相同外,已经是完完全全的改变了,虽然你仍能在
ASP.NET 中发现你熟悉的 SessionApplication 等等对象,但是不要尝试将他们同远古的
ASP 时代的 Session 等等画上等号,他们的实现机制和技术细节都已经是完全的不同了。
让我们来慢慢的深入到
ASP.NET Framework 的核心内部,看看它是如何实现的,看看她是如何能承担起下一代 Web 开发技术平台这个美誉的。
4.1、
ASP.NET Framework 深度历险 – 一个 HTTP 请求的生命周期 在基于 WEB 的应用程序开发当中,其核心就是在客户端机器以及服务器端机器之间通过 HTTP 协议相互传递需要的信息,因此,了解一个 WEB 服务器是如何处理 HTTP 请求的就显得格外的重要了。
深入的理解一个 HTTP 请求的生命周期对于今后的 WEB 应用程序开发也是格外重要的。
在本节,我们就一同来了解一下一个 HTTP 请求的生命周期吧。
4.1.1 回顾
ASP 时代的 HTTP 请求处理过程 在这一小节内,我们来跟随考古学家共同了解一下古老的
ASP 运行机制:4 第四章
ASP.NET Framework 深度历险 (uestc95263.net) 在以前的
ASP 时代,当你请求一个.
asp 文件的时候,这个 HTTP 请求首先会被一个名为 inetinfo.exe 进程所截获,这个 inetinfo.exe 进程实际上就是WWW 服务进程,在截获这个 HTTP 请求之后它会将这个请求转交给
asp.dll 进程,
asp.dll 进程就会解释执行这个
asp 叶面, 然后将解释后的数据流返回给客户端浏览器。
我们可以通过下面的图来清楚的了解古老的
ASP 时代,服务器进程是如何处理来自客户端的 HTTP 请求的: 图 4.1.1
ASP 时代处理 HTTP 请求 其实
ASP.DLL 是一个依附在 IIS 的 ISAPI 文件,它负责了对诸如
ASP 文件ASA 文件等等的解释执行,我们可以在 IIS 配置选项看到它是如何配置的: 第四章
ASP.NET Framework 深度历险 5图 4.1.2 ISAPI 配置 通过图 4.1.2,我们可以看到红色标记的部分,它清楚的告诉我们 IIS 会将对.
asp 以及.asa 等文件的 HTTP 请求转交给
asp.dll 这个 ISAPI 文件来处理。
在古老的
ASP 时代,针对这些
asp 等文件的 HTTP 请求被
asp.dll 所截获,从而被解释执行。
真是由于解释执行,每一次处理这些文件都是从头来过进行一番痛苦的解释才能执行使得 IIS 在处理大流量的 HTTP 请求的时候并不是得心应手,这也是
ASP 时代最大的弊端。
4.1.2
ASP.NET 时代的 HTTP 请求处理方式 看完了远古时代
ASP 的 HTTP 请求处理方法,转过头来我们看看如今的
ASP.NET Framework 是如何处理一个 HTTP 请求的。
同 当客户端向 WEB 服务器请求一个.aspx 文件的时候,
ASP 类似,这个 HTTP请求也会被 inetinfo.exe 进程(因为它就是 WWW 服务)截获,它判断文件的后缀之后, 而 将这个请求转交给 ASPNET_ISAPI.dll, ASPNET_ISAPI.dll 则会通过一个被称为 Http PipeLine 的管道,将这个 HTTP 请求发送给 ASPNET_WP.exe 进程,当这个 HTTP 请求进入 ASPNET_WP.exe 进程之后,
ASP.NET Framework 就会通过 HttpRuntime 来处理这个 HTTP 请求,处理完毕将结果返回客户端。
6 第四章
ASP.NET Framework 深度历险 (uestc95263.net) 接下来我们同样来看看一个描述
ASP.NET Framework 是如何处理 HTTP 请求的图示: 图 4.1.3
ASP.NET Framework 处理 HTTP 请求 通过上面的图我们可以清楚的了解到
ASP.NET Framework 是如何接收、处理一个 HTTP 请求的完整过程。
不过也许有些读者在看完上面的图示之后就会有这样的疑问了:
ASP.NETFramework 处理一个 HTTP 请求的流程和以前的
ASP 时代好像并没有太大的改进啊。
不要着急,在
ASP.NET Framework 中我们甚至能够了解到 HttpRuntime 的细节。
接下来我们继续在
ASP.NET Framework 的世界深入历险下去。
继续深入的第一步当然是要到 HttpRuntime 的世界瞧一瞧了。
4.1.3 深入
ASP.NET Framework HTTP 运行时 在本节我们会一同看看 HTTP 运行时内部的一些东西。
当一个 HTTP 请求被送入 HttpRuntime 之后,这个 HTTP 请求会继续被送入到一个被称之为 HttpApplication Factory 的一个容器当中,而这个容器会给出一个 HttpApplication 实例来处理传递进来的 HTTP 请求,而后这个 HTTP 请求会依次进入如下几个容器中: HttpModule HttpHandler Factory HttpHandler 当系统内部的 HttpHandler 的 ProcessResquest 方法处理完毕之后,整个Http Request 就被处理完成了,客户端也就得到相应的东东了。
ASP.NET Framework 处理 HTTP 请求的流程示意图如下: 第四章
ASP.NET Framework 深度历险 7图 4.1.4 现在我们可以整理一下完整的一个 HTTP 请求在
ASP.NET Framework 下是如何被处理的:HttpRequest inetinfo.exe ASPNET_ISAPI.dll Http PipelineASPNET_WP.exe HttpRuntime HttpApplication FactoryHttpApplication HttpModule HttpHandler Factory HttpHandlerHttpHandler.ProcessRequest 读者或许会问,我知道这个 HTTP 请求处理流程有什么用处呢?当然有用了,比如如果你想要中途截获一个 Http Request 并且做些自己的处理,该如何做呢? 通过仔细的对上面图示的观察,读者应当知道能够在什么地方截获到这个HTTP 请求。
对,就是在 HTTPRuntime 运行时内部来做到这一点的,确切的说,是在 HttpModule 这个容器中做到这一点的。
既然已经进入了神奇的HttpRuntime 内部世界, 我们当然要多看几眼了, 接下来我们会继续我们的历险步伐,向 HttpModule 容器进军!8 第四章
ASP.NET Framework 深度历险 (uestc95263.net) 4.2、
ASP.NET Framework 深度历险 – HttpModule 是如何工作的? 我们已经知道 HttpModule 容器是一个 HTTP 请求的必经之路,而我们在此小节的任务就是在 HttpModule 的内部深入看个究竟。
4.2.1 HttpModule 在
ASP.NET Framework 中的位置 我们上回说到,一个来自于客户端的 HTTP 请求被截获后经过层层转交(怎么都在踢皮球?呵呵)到达了 HttpModule 这个“请求监听器”。
HttpModule就类似于安插在 ASPNET_WP.EXE 进程中的一个窃听器,稍微有些常识的人都会很自然的想象得到窃听器是用来做什么的, 而我们的 HttpModule 可以说是作窃听器的绝好人选了,但是需要明确的是,HttpModule 绝对不是简单的监听器,它可以做到更多的东西,比如它可以对截获的请求增加一些内容等等。
那么 HttpModule 在整个
ASP.NET Framework 中的位置处在哪里呢?下面我们通过图示来看看: 第四章
ASP.NET Framework 深度历险 9 图 4.2.1 HttpModule 的位置 另外需要明白的是, 当一个 HTTP 请求到达 HttpModule 的时候,整个
ASP.NETFramework 系统还并没有对这个 HTTP 请求做任何的真正处理,也就是说此时对于 HTTP 请求来讲,HttpModule 只是它路过的一个地方而以。
但是正是因为HttpModule 是一个 HTTP 请求的“必经之路”,所以我们可以在这个 HTTP 请求传递到真正的请求处理中心(HttpHandler)之前附加一些我们需要的信息在这个 HTTP 请求信息之上,或者针对我们截获的这个 HTTP 请求信息作一些额外的工作,或者在某些情况下干脆终止满足一些条件的 HTTP 请求,从而可以起到一个 Filter 过滤器的作用,而不仅仅是一个窃听器了。
通过查阅 MSDN(不要去相信.NET SDK 自带的那个 QuickStarts Web 文档,正式版本中竟然在很多地方没有更新这个文档,很多东西在正式版本是无效的),你会发现系统 HttpModule 实现了一个叫做 IHttpModule 的接口, 很自然的就应当想到,只要我们自己的类能够实现 IHttpModule 接口,不就可以完全替代系统的 HttpModule 了吗?完全正确。
在我们开始自己的 HttpModule 类之前,我先来告诉你系统中的那个HttpModule 是什么样子的,
ASP.NET 系统中默认的 HttpModule 有以下几个: System.Web.Caching.OutputCacheModule System.Web.SessionState.SessionStateModule System.Web.Security.WindowsAuthenticationModule System.Web.Security.FormsAuthenticationModule System.Web.Security.PassportAuthenticationModule System.Web.Security.UrlAuthorizationModule System.Web.Security.FileAuthorizationModule 这些系统默认的 HttpModule 是在文件 machine.config 中配置,这个文件位于你安装的.NET 框架所在的目录中,比如在你的系统文件目录中的C:\WINNT\Microsoft.NET\Framework\v1.0.3705\CONFIG\machine.config。
在我们开发
ASP.NET 应用程序的时候会频繁的使用到一个 web.config 配置文件,那么这个 machine.config 和我们常见的 web.config 有什么关系呢?原来在
ASP.NET Framework 启动处理一个 Http Request 的时候,她会依次加载machine.config 以及你请求页面所在目录的 web.config 文件, 里面的配置是有ltremovegt标签的,什么意思不说也知道了吧。
如果你在 machine.config 中配置了一个自己的 HttpModule,你仍然可以在离你最近 web.config 文件中“remove”掉这个映射关系。
10 第四章
ASP.NET Framework 深度历险 (uestc95263.net)4.2.2 构建我们自己的 HttpModule 在上一小节中,我们谈到了系统默认的各个 HttpModule 均继承实现了一个叫做 IhttpModule 的接口。
我们先来看看这个接口的真实面目吧:语法:public interface IHttpModule需求:名称空间: System.Web平台: Windows 2000 Windows XP ProfessionalWindows .NET Server family装配件: System.Web in System.Web.dll公共成员方法:void Dispose参数:无返回值:void作用:销毁不再被 module 所使用的资源。
void InitHttpApplication context参数:HttpApplication 类型的实例返回值:void作用:初始化一个 module,为捕获 HTTP 请求做出一些准备。
了解了接口 IhttpModule 的方法,我们也就知道了如何去实现它了。
接下来,我们来开始我们自己的 HttpModule 构建历程吧。
1 打开 VS.NET 新建一个“Class Library”项目,将它命名为 MyHttpModule。
2 引用 System.Web.dll 文件 在代码区域敲入:using Systemusing System.Webnamespace MyHttpModuleTest /// ltsummarygt /// 说明:用来实现自定义 HttpModule 的类 /// 作者:uestc95 /// 联系:uestc95263.net /// lt/summarygt public class MyHttpModule:IHttpModule 第四章
ASP.NET Framework 深度历险 11 /// ltsummarygt /// 说明:构造器方法 /// 作者:uestc95 /// 联系:uestc95263.net /// lt/summarygt public MyHttpModule /// ltsummarygt /// 说明:实现 IHttpModule 接口的 Init 方法 /// 作者:uestc95 /// 联系:uestc95263.net /// lt/summarygt /// ltparam namequotapplicationquotgtHttpApplication 类型的参数lt/paramgt public void InitHttpApplication application application.BeginRequest newEventHandlerthis.Application_BeginRequest application.EndRequest newEventHandlerthis.Application_EndRequest /// ltsummarygt /// 说明:自己定义的用来做点事情的私有方法 /// 作者:uestc95 /// 联系:uestc95263.net /// lt/summarygt /// ltparam namequotobjquotgt传递进来的对象参数lt/paramgt /// ltparam namequotequotgt事件参数lt/paramgt private void Application_BeginRequestObject objEventArgs e //声明 HttpApplication HttpApplication applicationHttpApplicationobj HttpContext contextapplication.Context HttpResponse responsecontext.Response12 第四章
ASP.NET Framework 深度历险 (uestc95263.net) HttpRequest requestcontext.Request response.Writequot我来自 Application_BeginRequest:quot /// ltsummarygt /// 说明:自己定义的用来做点事情的私有方法 /// 作者:uestc95 /// 联系:uestc95263.net /// lt/summarygt /// ltparam namequotobjquotgt传递进来的对象参数lt/paramgt /// ltparam namequotequotgt事件参数lt/paramgt private void Application_EndRequestObject objEventArgs e HttpApplication applicationHttpApplicationobj HttpContext contextapplication.Context HttpResponse responsecontext.Response HttpRequest requestcontext.Request response.Writequot我来自 Application_EndRequest:quot) /// ltsummarygt /// 说明:实现 IHttpModule 接口的 Dispose 方法 /// 作者:uestc95 /// 联系:uestc95263.net /// lt/summarygt public void Dispose 3)在 VS.NET 中编译之后,你会得到 MyHttpModule.dll 这个文件。
4)接下来我们的工作就是如何让 ASPNET_WP.exe 进程将 http request 交给我们自己写的这个 HttpModule 呢?方法就是配置 web.config 文件。
在 web.config 文件中增加如下几句话: lthttpModulesgt ltadd namequottestquot 第四章
ASP.NET Framework 深度历险 13typequotMyHttpModuleTest.MyHttpModuleMyHttpModulequot/gt lt/httpModulesgt 注意要区分大小写,因为 web.config 作为一个 XML 文件是大小写敏感的。
“typeMyHttpModuleTest.MyHttpModuleMyHttpModule”告诉我们,系统将会将 HTTP 请求交给位于 MyHttpModule.dll 文件中的MyHttpModuleTest.MyHttpModule 类去处理。
而这个 DLL 文件系统将会自动到\bin 子目录或者系统全局程序集缓冲区(GAC)搜寻。
我们可以将我们刚才得到的 DLL 文件放在 bin 子目录中,至于后者,你可以通过.NET SDK 正式版自带的Config 工具做到,我们不详细说了。
好了,我们的用来截获 HTTP 请求的自定义 HttpModule 就完成并且装配完成了,你可以试着在你的 web 项目中建立一个新的 WebForm,运行看看呢?:) 最后,我们假设一个使用这个 HttpModule 的场合。
A 站点提供免费的
ASP.NET 虚拟空间给大家, 但是 A 站点的管理者并不想提供免费的午餐,他想要在每一个页面被浏览的时候自动弹出自己公司的广告,我总不能时刻监视所有用户的所有页面吧,并且想要在每一个页面手动添加一段 JS 代码,工作量之大是不可想象的,也是非常不现实的。
那么好了,只要我们的 HttpModule 一旦被挂接完成,这一切都将是轻而易举的事情了,只要我们在每一个 HTTP 请求被我们捕获的时候,给他在 HTTP 请求信息上面附加上一些我们自己的写的JavaScript 代码就好了! 我们上面提到在 Init方法中使用了两个事件 BeginRequest 和EndRequest,这两个事件分别是 Init中可以处理的所有事件的最开始事件和最终事件,在他们中间还有一些其它的事件可以被我们利用,可以查阅 MSDN,这一点我们会在下面详细的讲解。
在下一节开始之前,我们请各位读者考虑这样一个问题:在 HttpModule 中可以正常使用 ResponseRequestServerApplication 对象吗?请读者自行试着写一些代码看看行不行。
在 还有一个问题就是, HttpModule 中能操作 Session对象吗?如果可以或者不可以,请仔细思考一下原因何在,在下一节我们会详细的探讨这个问题。
4.2.3 深入 HttpModule 历险 在这一小节中,我们会详细的探讨有关 HttpModule 的运行机制,从而使得各位读者能够透彻的了解 HttpModule 是如何控制 HTTP 请求的。
我们在上一节曾经提及当一个 HTTP 请求被
ASP.NET Framework 捕获之后会依次交给 HttpModule 以及 HttpHandler 来处理,但是需要明确的是,不能理解为 HttpModule 和 HttpHandler 是完全独立的。
实际上是,在 HTTP 请求在HttpModule 传递的过程中会在某个事件内将控制权交给 HttpHandler 的,而真正的处理在 HttpHandler 中执行完成之后,HttpHandler 会再次将控制权交还给14 第四章
ASP.NET Framework 深度历险 (uestc95263.net)HttpModule。
也就是说 HttpModule 在某个请求经过她的时候会在恰当时候同HttpHandler 进行通信,在何时,如何通信呢?这就是下面提到的了。
我们在上一小节提到在 HttpModule 容器中最开始的事件是 BeginRequest,最终的事件是 EndRequest。
你如果仔细看上次给出的源程序的话,应当发现在方法 Init中参数我们传递的是一个 HttpApplication 类型,而我们曾经提及的两个事件正是这个传递进来的 HttpApplication 的事件之一。
HttpApplication 还有其它众多的事件,分别如下: application.BeginRequest 事件 application.EndRequest 事件 application.PreRequestHandlerExecute 事件 application.PostRequestHandlerExecut.