ASP.NET Framework深度历险(1-3)(转)

 

转(http://www.cnblogs.com/veelee/archive/2004/08/30/37745.html)
记得前一段时间有本不错的书叫Delphi深度历险,写得不错,我也就暂且借用了,:)
在这里我不打算简单介绍ASP.NET的入门知识了,ASP.NET除了名字和古老的ASP有些相同外,已经是完完全全的改变了,虽然你仍能在ASP.NET中发现你熟悉的 Session,Application等等,但是不要尝试将他们同远古的ASP时代的Session等等画上等号。
我们来慢慢的深入到ASP.NET Framework的核心内部,看看她是如何实现的,看看她是如何能承担起下一代Web开发技术平台这个美誉的。
这篇东东不曾想过要完成多少章节,也没有这个必要,权当日记的形式存在,或许很短,或许很长,我会尽我的所能来将ASP.NET Framework展现在诸位面前。
如果你对ASP.NET Framework没有任何了解,你同样可以成为ASP.NET coding高手,如果是这样,你就不必继续看下去了。

Chapter One --  Process a http request.

我们瞧一瞧ASP.NET Framework的运行机制和架构。
在开始之前,我们先跟随考古学家参观一下古老的ASP运行机制:
当你请求一个*.asp文件的时候,这个http request首先被inetinfo.exe进程所截获,这个inetinfo.exe进程就是WWW服务进程,然后她会将这个请求转交给 asp.dll进程,asp.dll进程就会解释执行这个asp叶面,然后将解释后的数据流返回给客户端浏览器。

转过头来我们看看如今的ASP.NET Framework是如何处理一个http request.
当你请求一个*.aspx文件的时候,同样的这个http request会被inetinfo.exe进程截获,她判断文件的后缀之后,将这个请求转交给 ASPNET_ISAPI.dll,ASPNET_ISAPI.dll会通过一个被称为Http PipeLine的管道,将请求发送给ASPNET_WP.exe进程,当这个http request进入ASPNET_WP.exe进程之后,会通过HttpRuntime来处理这个请求,处理完毕将结果返回客户端。

OK,好像并没有太大的改进嘛,不要着急,在ASP.NET Framework中我们甚至能够了解到HttpRuntime的细节。好,继续深入下去:
当Http Request进入HttpRuntime之后,会继续进入到一个被称之为HttpApplication Factory的一个Container中,她会给出一个HttpApplication来处理传递进来的请求,这个请求会依次进入如下几个 Container:HttpModule->HttpHandler Factory->HttpHandler。
当系统内部的HttpHandler的ProcessResquest方法处理完毕之后,整个Http Request就完成了,客户端也就得到相应的东东了。

整理一下ASP.NET Framework处理一个Http Request的流程:

HttpRequest-->inetinfo.exe-->ASPNET_ISAPI.dll-->Http Pipeline-->ASPNET_WP.exe-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest()

或许会问,我知道这个处理流程有什么用处呢?当然有用了,比如如果你想要中途截获一个Http Request并且做些自己的处理,该如何做呢?这是下一次我们探讨的东东了,下次我们详细讨论处理的细节问题。
see you later
.NET Framework Version:1.0.3705正式版
VS.NET(C#) Version:7.0.9466正式版


    刚刚吃完晚饭,正好在键盘上面锻炼一下手指。
    接着上回继续写这个“日记”:
   
    Chapter Two -- HttpModule是如何工作的?

    我们上回说到,一个来自于客户端的Http Request被截获后经过层层转交(怎么都在踢皮球?呵呵)到达了HttpModule这个“请求监听器”。
HttpModule就类似于安插在ASPNET_WP.EXE进程中的一个听器,稍微有些常识的人都会很自然的想象得到切听器是用来做什么的,而我们的HttpModule
可以说是作切听器的绝好人选了,但是需要明确的是,HttpModule绝对不是简单的监听器,它可以做到更多的东西,比如它可以对截获的请求增加一些内容
等等。
     另外需要明白的是,当一个Http Request到达HttpModule的时候,整个ASP.NET Framework系统还并没有对这个请求做任何的真正处理,但是我们可以
在这个Http Request传递到真正的请求处理中心(HttpHandler)之前附加一些我们需要的信息在这个Http Request至上,或者针对我们截获的这个Http
Request信息作一些额外的工作,或者在某些情况下干脆终止满足一些条件的Http Request,从而可以起到一个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构建历程吧。
     1)打开VS.NET新建一个“Class Library”项目,将它命名为MyHttpModule。
     2)引用System.Web.dll文件

     在代码区域敲入:

using System;
using System.Web;

namespace MyHttpModuleTest
{
    /// <summary>
    /// 说明:用来实现自定义HttpModule的类
    /// 作者:uestc95
    /// 联系:uestc95@263.net
    /// </summary>
    public class MyHttpModule:IHttpModule
    {
        /// <summary>
        /// 说明:构造器方法
        /// 作者:uestc95
        /// 联系:uestc95@263.net
        /// </summary>
        public MyHttpModule()
        {
            
        }

        /// <summary>
        /// 说明:实现IHttpModule接口的Init方法
        /// 作者:uestc95
        /// 联系:uestc95@263.net
        /// </summary>
        /// <param name="application">HttpApplication类型的参数</param>
        public void Init(HttpApplication application)
        {
            application.BeginRequest +=new EventHandler(this.Application_BeginRequest);
            application.EndRequest +=new EventHandler(this.Application_EndRequest);
        }

        /// <summary>
        /// 说明:自己定义的用来做点事情的私有方法
        /// 作者:uestc95
        /// 联系:uestc95@263.net
        /// </summary>
        /// <param name="obj">传递进来的对象参数</param>
        /// <param name="e">事件参数</param>
        private void Application_BeginRequest(Object obj,EventArgs e)
        {
            HttpApplication application=(HttpApplication)obj;
            HttpContext context=application.Context;
            HttpResponse response=context.Response;
            HttpRequest request=context.Request;

            response.Write("我来自Application_BeginRequest,:)");
            
        }

        /// <summary>
        /// 说明:自己定义的用来做点事情的私有方法
        /// 作者:uestc95
        /// 联系:uestc95@263.net
        /// </summary>
        /// <param name="obj">传递进来的对象参数</param>
        /// <param name="e">事件参数</param>
        private void Application_EndRequest(Object obj,EventArgs e)
        {
            HttpApplication application=(HttpApplication)obj;
            HttpContext context=application.Context;
            HttpResponse response=context.Response;
            HttpRequest request=context.Request;

            response.Write("我来自Application_EndRequest,:)");
            
        }

        /// <summary>
        /// 说明:实现IHttpModule接口的Dispose方法
        /// 作者:uestc95
        /// 联系:uestc95@263.net
        /// </summary>
        public void Dispose(){}
    }
}

     3)在VS.NET中编译之后,你会得到MyHttpModule.dll这个文件。
     4)接下来我们的工作就是如何让ASPNET_WP.exe进程将http request交给我们自己写的这个HttpModule呢?方法就是配置web.config文件。
     在web.config文件中增加如下几句话:
        <httpModules>
            <add name="test" type="MyHttpModuleTest.MyHttpModule,MyHttpModule"/>
        </httpModules>
      注意要区分大小写,因为web.config作为一个XML文件是大小写敏感的。“type=MyHttpModuleTest.MyHttpModule,MyHttpModule”告诉我们
      系统将会将http request请求交给位于MyHttpModule.dll文件中的MyHttpModuleTest.MyHttpModule类去处理。而这个DLL文件系统将会自动
      到bin子目录或者系统全局程序集缓冲区(GAC)搜寻。我们可以将我们刚才得到的DLL文件放在bin子目录中,至于后者,你可以通过.NET SDK正式版
      自带的Config工具做到,我们不详细说了。


      好了,我们的用来截获http request请求的HttpModule就完成并且装配完成了,你可以试着在你的web项目中建立一个新的WebForm,运行看看呢?:)
      最后,我们假设一个使用这个HttpModule的场合。A站点提供免费的ASP.NET虚拟空间给大家,但是A站点的管理者并不想提供免费的午餐,他想要在每一个
      页面被浏览的时候自动弹出自己公司的广告(就像现在的www.X63.com一样),我总不能时刻监视所有用户的所有页面吧,并且想要在每一个页面手动添加
      一段JS代码,工作量是不可想象的,也是不现实的。那末好了,只要我们的HttpModule一旦被挂接完成,这一切都将是轻而易举的事情了,只要我们在每一个
      Http Request被我们捕获的时候,给他增加上一些JS就好了!

      我们上面提到在Init()方法中使用了两个事件BeginRequest和EndRequest,这两个事件分别是Init()中可以处理的所有事件的最开始事件和最终事件,在他们
      中间还有一些其它的事件可以被我们利用,可以查阅MSDN。

      另外在我关闭EditPlus之前,需要敲下如下的话:
      在HttpModule中可以正常使用Response,Request,Server,Application,但是不能操作任何与Session有关代码!
      为什么呢?自己考虑一下吧,下回看看原因在哪里,另外再给出一个问题,你能发现系统默认的那几个HttpModule在哪里配置的呢?找找看。
      
      下回我们看看HttpHandler部分以及如何同HttpModule相配合的东东。

      See you later.

 

 

 

Author:uestc95
ArticleType:原创
E-mail:uestc95@263.net
.NET Framework Version:1.0.3705正式版
VS.NET(C#) Version:7.0.9466正式版



    这几天胃口还算好,虽然算不上“吃嘛嘛香”,但是也算是不错了,但愿能增上几斤才好。
    怎么样,我们在Chapter Two最后提出的两个问题估计早出来了吧,:)
    First:为什么在HttpModule中不能使用Session?
    Second:系统默认的几个HttpModule在哪里配置的?

    我们先挑软柿子捏,第二个问题的答案是:在文件machine.config中配置,比如在你的系统文件目录中的C:WI

NNTMicrosoft.NETFrameworkv1.0.3705CONFIGmachine.config。
    虽然是个软柿子,但是还是有些东东在里面的,那就是machine.config和我们常见的web.config有什么关

系呢?在ASP.NET Framework启动处理一个Http Request的时候,她会依次加载machine.config以及你请求页面

所在目录的web.config文件,里面的配置是有<remove>标签的,什么意思不说也知道了吧。如果你在machine.c

onfig中配置了一个自己的HttpModule,你仍然可以在离你最近web.config文件中“remove”掉这个映射关系。
    至于第一个问题,呵呵,如果你仔细的运行过上次的文件但是没有自己仔细深入研究一下的话,一定会觉

得在HttpModule中的确无法使用Session,:)。如果你发现上次提出的问题本身就是一个"Problem",那么恭喜你,你没有掉进我故意给出的框框中,并且很有质疑精神,:)
    今天我们就来解释一下HttpModule和HttpHandler之间的关系,在今天的“日记”完成的时候,你也就会发现第一个问题的答案了。

    Chapter Three -- 深入HttpModule

    我们曾经提及当一个Http Request被ASP.NET Framework捕获之后会依次交给HttpModule以及HttpHandler来处理,但是不能理解为HttpModule和 HttpHandler是完全独立的,实际上是,在Http Request在HttpModule传递的过程中会在某个事件内将控制权交给HttpHandler的,而真正的处理在HttpHandler中完成之后,再将控制权交还给HttpModule。也就是说HttpModule在某个请求经过她的时候会再恰当时候同HttpHandler进行通信,在何时,如何通信呢?这就是下面提到的了。
    我们提到在HttpModule中最开始的事件是BeginRequest,最终的事件是EndRequest。你如果仔细看上次给出的源程序的话,应当发现在方法Init()中参数我们传递的是一个HttpApplication类型,而我们曾经提及的两个事件正是这个传递进来的 HttpApplication的事件之一。
    HttpApplication还有其它的事件,分别如下:
            application.BeginRequest      //开始一个请
            application.EndRequest        //结束一个请求
            application.PreRequestHandlerExecute  //成功执行一个请求
            application.PostRequestHandlerExecute //成功回传一个请求
            application.ReleaseRequestState //释放请求状态
            application.AcquireRequestState //获得请求状态
            application.AuthenticateRequest //鉴别请求
            application.AuthorizeRequest //批准请求
            application.ResolveRequestCache //释放请求缓存
            application.PreSendRequestHeaders //成功发送请求headers
            application.PreSendRequestContent //成功发送请求content

      需要注意的是,在事件EndRequest之后还会继续执行application.PreSendRequestHeaders以及 application.PreSendRequestContent事件,而这两个事件大家想必应当从名称上面看得出来事做什么用途的了吧。是的,一旦触发了这两个事件,就表明整个Http Request的处理已经完成了,在这两个事件中是开始向客户端传送处理完成的数据流了。看到这里,您应当有一个疑问才对:怎么没见到 HttpHandler就处理完成了?不是提到过HttpHandler才是真正处理Http Request的吗?如果你有这个疑问表明你是仔细在看,也不枉我打字打得这莫累,:)。
    其实一开始我就提到了,在一个http request在HttpModule传递过程中,会在某一个时刻(确切的说应当是事件)中将这个请求传递给HttpHandler的。这个事件就是 ResolveRequestCache,在这个事件之后,HttpModule会建立一个HttpHandler的入口实例(做好准备了,:)),但是此时并没有将控制权交出,而是继续触发AcquireRequestState以及PreRequestHandlerExecute事件(如果你实现了的话)。看到了吗,最后一个事件的前缀是Pre,呵呵。这表明下一步就要进入HttpHandler了,的确如此,正如我们猜想的那样,在 PreRequestHandlerExecute事件之后,HttpModule就会将控制权暂时交给HttpHandler,以便进行真正的http request处理工作。而在HttpHandler内部会执行ProcessRequest来处理请求。在HttpHandler处理完毕之后,会将控制权交还给HttpModule,HttpModule便会继续对处理完毕的http Request进行层层的转交动作,直到返回到客户端。
   怎么样,是不是有些混乱?呵呵,苦于纯文本无法画流程图,我手头上已经画好了一个整个HttpModule的生命周期图,我只能暂且在这里用字符描绘一下前后流程了,如果你想要这个图片,可以给我发送mail,我给你mail过去。
   Http Request在整个HttpModule中的生命周期图:

                    Http Request开始
                        |
                       HttpModule
                            |
                     HttpModule.BeginRequest()
                        |
                HttpModule.AuthenticateRequest() //鉴别请求
                        |
                      HttpModule.AuthorizeRequest() //批准请求
                        |
                  HttpModule.ResolveRequestCache() //释放请求缓存
                        |
                    建立HttpHandler控制点
                        |
                接着处理(HttpHandler已经建立,此后Session可用)
                        |
                  HttpModule.AcquireRequestState() //获得请求状态
                        |
                HttpModule.PreRequestHandlerExecute() //成功执行请求handler
                        |
                   进入HttpHandler处理HttpRequest
                        |
                    HttpHandler.ProcessRequest()  //真正处理请求
                        |
            返回到HttpModule接着处理(HttpHandler生命周期结束,Session失效)
                        |
                HttpModule.PostRequestHandlerExecute() //处理回传请求handler
                        |
                HttpModule.ReleaseRequestState()  //释放请求状态
                        |
                HttpModule.UpdateRequestCache()  //更新请求缓存
                        |
                    HttpModule.EndRequest()  //请求结束
                        |
                HttpModule.PreSendRequestHeaders()  //成功回传请求header
                        |
                HttpModule.PreSendRequestContent() //成功回传请求内容
                        |
                    将处理后的数据返回客户端
                        |
                         整个Http Request处理结束
   
   
     怎么样,从上面的图中应当找到上次我们提出的第一个问题的答案了吧。
     为了验证上面的流程,我们可以用下面的这个自己的HttpModuel来验证一下就知道了。
     注意我们下面给出的是类的内容,框架还是前次我们给出的那个,自己加上就好了:

        public void Init(HttpApplication application)
        {
            application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
            application.EndRequest += (new EventHandler(this.Application_EndRequest));
            application.PreRequestHandlerExecute +=(new EventHandler(this.Application_PreRequestHandlerExecute));
            application.PostRequestHandlerExecute  +=(new EventHandler(this.Application_PostRequestHandlerExecute));
            application.ReleaseRequestState  +=(new EventHandler(this.Application_ReleaseRequestState));
            application.AcquireRequestState +=(new EventHandler(this.Application_AcquireRequestState));
            application.AuthenticateRequest +=(new EventHandler(this.Application_AuthenticateRequest));
            application.AuthorizeRequest +=(new EventHandler(this.Application_AuthorizeRequest));
            application.ResolveRequestCache +=(new EventHandler(this.Application_ResolveRequestCache));
            application.PreSendRequestHeaders +=(new EventHandler(this.Application_PreSendRequestHeaders));
            application.PreSendRequestContent +=(new EventHandler(this.Application_PreSendRequestContent));
        }
   
        private void Application_PreRequestHandlerExecute(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_PreRequestHandlerExecute<br>");
        }

        private void Application_BeginRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_BeginRequest<br>");
        }
   
        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_EndRequest<br>");

        }        
   
        private void Application_PostRequestHandlerExecute(Object source,EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_PostRequestHandlerExecute<br>");

        }

        private void Application_ReleaseRequestState(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_ReleaseRequestState<br>");

        }

        private void Application_UpdateRequestCache(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_UpdateRequestCache<br>");

        }

        private void Application_AuthenticateRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_AuthenticateRequest<br>");

        }

        private void Application_AuthorizeRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_AuthorizeRequest<br>");

        }

        private void Application_ResolveRequestCache(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_ResolveRequestCache<br>");

        }

        private void Application_AcquireRequestState(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_AcquireRequestState<br>");

        }

        private void Application_PreSendRequestHeaders(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_PreSendRequestHeaders<br>");

        }

        private void Application_PreSendRequestContent(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            context.Response.Write("Application_PreSendRequestContent<br>");

        }
        public void Dispose()
        {
        }


       好了,手累的不行了,:)
       老规矩,下面的问题仔细考虑:
       HttpModule中的Application的多个事件和Global.asax中的Application事件有联系吗?如果有,该会有哪些联系呢?

       下回会探讨HttpHandler的构建了,:)
       不过最近挺忙,不知道何时能继续......尽力吧。
       See you later.



  名称 说明
AcquireRequestState 当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生。Acquire(获得)
AuthenticateRequest 当安全模块已建立用户标识时发生。Authenticate(鉴别)
AuthorizeRequest 当安全模块已验证用户授权时发生。Authorize(批准)
BeginRequest 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生。
Disposed 在释放应用程序时发生。
EndRequest 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。
Error 当引发未处理的异常时发生。
LogRequest 恰好在 ASP.NET 为当前请求执行任何记录之前发生。
MapRequestHandler 基础结构。在选择了用来响应请求的处理程序时发生。
PostAcquireRequestState 在已获得与当前请求关联的请求状态(例如会话状态)时发生。
PostAuthenticateRequest 当安全模块已建立用户标识时发生。
PostAuthorizeRequest 在当前请求的用户已获授权时发生。
PostLogRequest 在 ASP.NET 处理完 LogRequest 事件的所有事件处理程序后发生。
PostMapRequestHandler 在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。
PostReleaseRequestState 在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生。
PostRequestHandlerExecute 在 ASP.NET 事件处理程序(例如,某页或某个 XML Web service)执行完毕时发生。
PostResolveRequestCache 在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。
PostUpdateRequestCache 在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件。
PreRequestHandlerExecute 恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。
PreSendRequestContent 恰好在 ASP.NET 向客户端发送内容之前发生。
PreSendRequestHeaders 恰好在 ASP.NET 向客户端发送 HTTP 标头之前发生。
ReleaseRequestState 在 ASP.NET 执行完所有请求事件处理程序后发生。该事件将使状态模块保存当前状态数据。
ResolveRequestCache 在 ASP.NET 完成授权事件以使缓存模块从缓存中为请求提供服务后发生,从而绕过事件处理程序(例如某个页或 XML Web services)的执行。
UpdateRequestCache 当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生

posted on 2009-05-18 17:02  冉元胜  阅读(216)  评论(0编辑  收藏  举报

导航