• ASP.NET应用程序生命周期综述
  • 阿迪王 发表于 2014/11/10 13:36:00 | 分类标签: 生命周期 asp.net 应用程序
  •  一般ASP.NET开发者对页面生命周期(PageLife Cycle)是比较熟悉的,在开发ASP.NET应用程序中经常需要从页面周期的角度去思考问题。实际上在页面生命周期的背后,还存在着一个不太为人所熟知的更广义的周期——应用程序生命周期(Application Life Cycle)。本篇文章将详细对其进行说明(IIS6.0)。

    应用程序生命周期综述

    所谓应用程序生命周期指的是:从客户端向Web服务器发出资源请求开始,到Web服务器反馈结果返回给客户端的整个在服务器端执行的过程。

    ASP.NET是Web 服务器下的 ISAPI 扩展,Web 服务器接收到请求时,会对所请求的文件的扩展名进行检查,确定应由哪个 ISAPI 扩展处理该请求,然后将该请求传递给合适的 ISAPI 扩展。ASP.NET处理已映射到其上的文件扩展名,如.aspx、.ascx、.ashx和.asmx。因此,aspx页面的请求是首先提交给IIS,然后由IIS通过文件扩展名查找ISAPI最终交给ASP.NET(aspnet_isapi.dll进程)进行处理,再经过一系列规范化的步骤最终生成对应的html编码返回给浏览器。综上可知,ASP.NET的页面生命周期是应用程序生命周期的一部分,而且仅仅当请求文件为aspx页面时才能触发该页面的生命周期。

    下面分阶段描述ASP.NET 应用程序生命周期(这里假设请求资源文件类型为aspx页面,下面的内容大部分是从bitfan前辈的博客文章和MSDN中复制而来):

     用户请求aspx页面:
     A, 当此请求到达Web服务器时,由HTTP.SYS(Windows进行http协议信息通讯的核心组件)负责接收,根据ISAPI扩展设置,将其传递给此ASP.NET应用程序所对应的应用程序池,在此应用程序池中运行的工作者进程负责处理请求。

           B, 工作者进程接收到这个请求之后,装载专用于处理ASP.NET页面的一个ISAPI扩展“aspnet_isapi.dll”,并将HTTP请求传给它。

           C, 工作者进程加载完aspnet_isapi.dll后,由aspnet_isapi.dll负责加载ASP.NET应用程序的运行环境――CLR

           D, 工作者进程工作于非托管环境(指Windows操作系统本身)之中,而.NET中的对象则工作于托管环境(指CLR)之中,aspnet_isapi.dll起到了一个沟通两者的桥梁作用,将收到的HTTP请求(由非托管环境传来)转发给相应.NET对象(处于托管环境中)处理。

    ASP.NET 应用程序(通常意义上的网站)接收第一个请求:

           A, 说明:同一个ASP.NET应用程序只在第一次请求时才会执行该阶段。
           B, 加载CLR之后,由System.Web.Hosting.ApplicationManager类负责创建一个应用程序域。
           C, 每个ASP.NET应用程序都运行于自己的应用程序域中,且对应着一个ApplicationManager类的实例对象,该对象负责对该应用程序进行管理,包括:激活和初始化 ASP.NET 应用程序、管理应用程序生存期和在应用程序中注册的对象的生存期、公开宿主环境使用的对象以处理 ASP.NET 应用程序请求、提供任意给定时刻运行于宿主进程中的应用程序的列表。
           D, 接着在应用程序域中创建一个System.Web.Hosting.HostingEnvironment实例对象,该对象提供对有关应用程序的信息(如存储该应用程序在服务器中的目录等等信息)的访问。
           E,关系图表示为:


    为每个请求创建 ASP.NET 核心对象:

            A, 当应用程序域创建完成之后,一个ISAPIRuntime对象被创建,并自动调用它的ProcessRequest()方法。在此方法中,ISAPIRuntime对象根据传入的HTTP请求创建一个HttpWorkerRequest对象,此对象以面向对象的方式包装了HTTP请求的各种信息(这就是说,原始的HTTP请求信息被封装为HttpWorkerRequest对象)
           B, 然后,调用ISAPIRuntime对象的StartProcessing()方法启动整个HTTP请求处理过程(此即“HTTP管线:HTTP Pipeline”),在这个处理过程的开端,一个HttpRuntime类型的对象被创建,前面创建好的HttpWorkerRequest对象作为方法参数被传送给此HttpRuntime对象的ProcessRequest()方法。
           C, HttpRuntime类的ProcessRequest()方法根据HttpWorkerRequest对象中所提供的HTTP请求信息,创建了一个HttpContext对象。HttpContext 类包含特定于当前应用程序请求的对象,如HttpRequest 和 HttpResponse 对象。
           D, HttpRequest 对象包含有关当前请求的信息,包括 Cookie 和浏览器信息。HttpResponse 对象包含发送到客户端的响应,包括所有呈现的输出和 Cookie。
           E,  在整个HTTP处理过程中,HttpContext对象都是可以访问的,也就是说这个阶段之后的应用程序生命周期内所有使用的HttpContext实际上都是这个HttpContext对象的引用,如aspx页面后台常用的Context属性。

    分配一个HttpApplication 对象用于处理请求:

           A, HttpRuntime类的ProcessRequest()方法除了创建HttpContext对象之外,还完成了另一个很重要的工作——向HttpApplicationFactory类的一个实例申请分配一个HttpApplication对象用于管理整个HTTP请求处理管线中的各种事件。
           B, HttpApplicationFactory对象负责管理一个HttpApplication对象池,当有HTTP请求到来时,如果池中还有可用的 HttpApplication对象,就直接分配此对象用于处理HTTP请求,否则,创建一个新的HttpApplication对象。
           C, 如果ASP.NET应用程序无 Global.asax 文件,使用原始的System.Web.HttpApplication类,如果ASP.NET应用程序中有使用在Global.asax中定义的继承于System.Web.HttpApplication的类,作为创建HttpApplication对象的类。

    HttpApplication对象启动HTTP管线:

           A, HttpApplication对象负责装配出整个“HTTP请求处理管线(HTTP Pipeline)”,可以将HttpContext看成是订单(HttpRequest)与产品(HttpResponse)的打包,而将HTTP请求处理管线看做是产品生成流程。在这个流程中,各个模块将根据订单(HttpRequest)提供的信息,将特定的内容添加到产品(HttpResponse)中。

           B, HttpContext对象经过“生产流水线”的不同部分时,HttpApplication对象会先后激发出一连串的事件。一些特定的组件——HTTP模块(HTTP Module)可以响应这些事件,在此事件响应代码中可以对HttpContext对象进行“加工和处理”。从这个意义上说,HTTP模块可以看成是“生产流水线”中的工人。

           C, HTTP模块对象是在HttpApplication对象的InitModules()方法中被创建的,一般在HTTP模块对象的Init方法内对HttpApplication对象所激发的特定事件进行绑定,在这些事件中,各个HTTP模块可以对HttpContext对象进行操作,主要是对HttpResponse进行添加内容,也能够直接将本次请求终止(这里需要注意的是,这里的终止并不会完全将HttpResponse返回空,而是跳过HttpApplication中的一些处理步骤,直接引发EndRequest事件)。
         
      D, ASP.NET提供了一些预定义的HTTP模块响应特定的事件,开发者也可以编写自己的HTTP模块并将其插入到“HTTP请求处理管线”中(可通过Web.Config配置)。
       
        E,  在HttpApplication对象触发PreRequestHandlerExecute事件之后,会根据请求调用合适的 IHttpHandler(或由IHttpHandlerFactory确定目标IHttpHandler)类的 ProcessRequest 方法,处理过程临时交给已配置的HttpHandler。这里只考虑aspx页面请求的话,那么本阶段将会调用aspx页面对应后台的继承于Page的类的ProcessRequest方法,从而触发页面生命周期开始执行。调用执行完成目标页面的生命周期之后,得到页面周期内呈现(Render)的html编码将加入到HttpContext对象的HttpResponse中。
       
        F,  调用请求页面的ProcessRequest 方法之后将会触发HttpApplication的PostRequestHandlerExecute事件,处理过程又交还给HttpApplication,直到触发PreSendRequestHenaders及PreSendRequestConternt。这时HttpApplication的任务算是完成了,HttpContext内的HttpResponse产品就是最终版了。

           G, HttpContext对象带着最后的处理结果来到了“HTTP请求处理管线”的未端,其信息被取出来,再次以aspnet_isapi.dll为桥梁传送给工作者进程。工作者进程再将HTTP请求的处理结果转给HTTP.SYS,由它负责将结果返回给浏览器。

    下面用流程图来直观地展示应用程序周期模型:




    以生产个性化面包对上述过程打个简单的比喻(仅参考,部分细节可能不符)

        1,  IIS就是整个面包店,所有参与的成员都是在IIS的环境下进行的。

        2,  HTTP.SYS是前台收费员,负责收集客户对产品的需求信息,包括请求资源的类型、地址等。
     
       3,  ISAPI负责联系各个面包房,有些负责生产ASP.NET面包,有些负责htm、shtml面包,还有其他生成各种类型面包的面包房。

        4,  工作者进程是大堂小二,负责前台收费员与面包房内沟通,还负责将面包房生成出来的面包交给前台收费员。
      
      5,  ASP.NET 应用程序是其中一种面包房,它可以生产aspx、asmx等类型的面包,由它根据前台收费员(HTTP.SYS)接到的订单进行生产。ASP.NET 面包房可以有多个,它们之间是独立运营的,如果其中某个ASP.NET面包房从来没有过客户订单,就没有配备相应的面点师傅了;只有当第一个订单来了,面包房才开始招师傅来干活了。
      
      6,  ApplicationManager对象是ASP.NET面包房的总负责人,他有对这个面包房的总控制权,包括什么时候开工,什么时候干脆关闭得了。

        7,  由ApplicationManager创建的应用程序域就是面包房内部的工作场所,所有的人员及设置都在里面。
      
      8,  HostingEnvironment对象是对这个面包房的说明书,各个设备在哪,怎么用等等信息,当然包括卫生合格证明啦,要不然就是违法生产了。

        9,  ISAPIRuntime对象及HttpRuntime对象就看做面包房负责人的命令吧,ISAPIRuntime是初步动员令,而HttpRuntime更是正式命令。

        10, HttpWorkerRequest对象是负责人在发布ISAPIRuntime命令的同时根据前台收费员提供的订单信息而组织的使面包房内部更容易理解的简要需求说明书。
       
     11, HttpContext对象则是负责人在发布HttpRuntime正式命令时,觉得HttpWorkerRequest还不太规范,因此将其修改为HttpContext对象。HttpContext是打包好的,里面有根据HttpWorkerRequest修改后的更详细的面包需求说明书HttpRequest,还有一个包装盒HttpResponse,里面是“基本”是空的,要求在剩下的步骤里将这个包装盒按照说明书的要求制作出准确的面包。

        12, HttpApplicationFactory是所有面包房的人力资源经理,由他分配由哪个师傅来接手面包房负责人的HttpContext打包。如果店里有手艺的师傅不够了,就再招一个;如果有师傅有闲余时间了,那得了,您上吧。
       
     13, HttpApplication对象就是人力资源经理分配的师傅了,这里称为“主师傅”,他相当于一个小工头。拿到HttpContext打包之后就开始在面包房了吼嗓子了:“我开始工作了!有谁要在HttpResponse中添加内容的赶紧了,过了就不候着了啊!”、“我要加载初始化Session了,之后Session就可以随便用了!”、“雏形师傅,该你大显身手了!”等等。主师傅的每次吼嗓子都代表着HttpApplication的每个事件,所有主师傅是最累最辛苦的角色了,因为他要吼二十几个嗓子,而且嗓门要大,好让HTTP模块对象们都能听见。

        14, HTTP模块对象的是装点师,装点师有多个,各个师傅的作用都不同;每个装点师在HttpApplication主师傅每次吼嗓子的时候都可以往HttpResponse中添加内容,撒点糖、加点装饰品什么的。装点师有一个很大的权利,他可以要求主师傅直接跳过一些步骤,直接吼“我的任务马上就要结束了!”。这里要注意的是,每次主师傅吼嗓子,装点师往HttpResponse加东西的顺序是固定的,谁来店里来早(就是在Web.config中的配置顺序),谁是前辈,谁就先操作;这一嗓子吼完之后,所有想要往HttpResponse加东西的的装点师都操作过之后,主师傅才会吼下一嗓子;各个装点师可以在主师傅吼完一嗓子之后不理他,等到他吼另一个嗓子的时候再行动,也就是说装点师傅的行动完全是自主的。

        15, IHttpHandler对象是特殊的一类师傅,是专业人才,这里称为雏形师傅(而IHttpHandlerFactory则是这些专业人才的介绍人什么的)。每个IHttpHandler对象只负责一种面包,他会按照自己的理解将这个面包做出来,然后放在主师傅的HttpResponse中。

        16, 这里有个流程需要特别注意,在吼完“雏形师傅,该你大显身手了!”之后,主师傅会将HttpContext打包临时交给IHttpHandler对象,等IHttpHandler对象的工作完成之后再交还给主师傅,之后主师傅会吼“雏形师傅的工作完成了!”。

    后续流程:等到主师傅吼完“面包已经完成了,我要把面包放到面包房的产品架上了!”,这时主师傅的工作已经结束了,这时大堂小二会到面包房的产品架去面包交给前台收费员,前台拿到面包后交个客户。这样,一次面包的定制过程就完成了。

        17,  后续流程:等到主师傅吼完“面包已经完成了,我要把面包放到面包房的产品架上了!”,这时主师傅的工作已经结束了,这时大堂小二会到面包房的产品架去面包交给前台收费员,前台拿到面包后交个客户。这样,一次面包的定制过程就完成了。


    下面的表格列举了HttpApplication在启动HTTP管线阶段依次触发的事件

    BeginRequest

    在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生

    AuthenticateRequest

    当安全模块已建立用户标识时发生。

    PostAuthenticateRequest

    当安全模块已建立用户标识时发生。

    AuthorizeRequest

    当安全模块已验证用户授权时发生。

    PostAuthorizeRequest

    在当前请求的用户已获授权时发生。

    ResolveRequestCache

    在 ASP.NET 完成授权事件以使缓存模块从缓存中为请求提供服务后发生,从而绕过事件处理程序(例如某个页或 XML Web services)的执行。

    PostResolveRequestCache

    在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。

    PostMapRequestHandler

    在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。

    AcquireRequestState

    当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生。

    PostAcquireRequestState

    在已获得与当前请求关联的请求状态(例如会话状态)时发生。

    PreRequestHandlerExecute

    恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。

    PostRequestHandlerExecute

    在 ASP.NET 事件处理程序(例如,某页或某个 XML Web service)执行完毕时发生。

    ReleaseRequestState

    在 ASP.NET 执行完所有请求事件处理程序后发生。 该事件将使状态模块保存当前状态数据。

    PostReleaseRequestState

    在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生。

    UpdateRequestCache

    当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生。

    PostUpdateRequestCache

    在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件。

    EndRequest

    在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。

    PreSendRequestHeaders

    恰好在 ASP.NET 向客户端发送 HTTP 标头之前发生。

    PreSendRequestContent

    恰好在 ASP.NET 向客户端发送内容之前发生。

  • 请您注意

    ·自觉遵守:爱国、守法、自律、真实、文明的原则

    ·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规

    ·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品

    ·承担一切因您的行为而直接或间接导致的民事或刑事法律责任

    ·您在编程中国社区新闻评论发表的作品,本网站有权在网站内保留、转载、引用或者删除

    ·参与本评论即表明您已经阅读并接受上述条款

  • 感谢本文作者
  • 作者头像
  • 昵称:阿迪王
  • 加入时间:2013/6/13 0:00:00
  • TA的签名
  • 这家伙很懒,虾米都没写
  • +进入TA的空间
  • 以下内容也很赞哦
分享按钮