ASP.NET 应用程序生命周期概述

下表描述了 ASP.NET 应用程序生命周期的各个阶段。

阶段说明

用户从 Web 服务器请求应用程序资源。

ASP.NET 应用程序的生命周期以浏览器向 Web 服务器(对于 ASP.NET 应用程序,通常为 IIS)发送请求为起点。ASP.NET 是 Web 服务器下的 ISAPI 扩展。Web 服务器接收到请求时,会对所请求的文件的文件扩展名进行检查,确定应由哪个 ISAPI 扩展处理该请求,然后将该请求传递给合适的 ISAPI 扩展。ASP.NET 处理已映射到其上的文件扩展名,如 .aspx、.ascx、.ashx 和 .asmx。

Note注意

如果文件扩展名尚未映射到 ASP.NET,则 ASP.NET 将不会接收该请求。对于使用 ASP.NET 身份验证的应用程序,理解这一点非常重要。例如,由于 .htm 文件通常没有映射到 ASP.NET,因此 ASP.NET 将不会对 .htm 文件请求执行身份验证或授权检查。因此,即使文件仅包含静态内容,如果希望 ASP.NET 检查身份验证,也应使用映射到 ASP.NET 的文件扩展名创建该文件,如采用文件扩展名 .aspx。

Note注意

如果要创建服务于特定文件扩展名的自定义处理程序,必须在 IIS 中将该扩展名映射到 ASP.NET,还必须在应用程序的 Web.config 文件中注册该处理程序。有关更多信息,请参见 HTTP 处理程序介绍

ASP.NET 接收对应用程序的第一个请求。

当 ASP.NET 接收到对应用程序中任何资源的第一个请求时,名为 ApplicationManager 的类会创建一个应用程序域。应用程序域为全局变量提供应用程序隔离,并允许单独卸载每个应用程序。在应用程序域中,将为名为 HostingEnvironment 的类创建一个实例,该实例提供对有关应用程序的信息(如存储该应用程序的文件夹的名称)的访问。

下面的关系图说明了这种关系:

应用程序环境概览图形

如果需要,ASP.NET 还可对应用程序中的顶级项进行编译,其中包括 App_Code 文件夹中的应用程序代码。有关更多信息,请参见本主题后面的“编译生命周期”。

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

创建了应用程序域并对 HostingEnvironment 对象进行了实例化之后,ASP.NET 将创建并初始化核心对象,如HttpContextHttpRequest 和 HttpResponseHttpContext 类包含特定于当前应用程序请求的对象,如 HttpRequest 和 HttpResponse对象。HttpRequest 对象包含有关当前请求的信息,包括 Cookie 和浏览器信息。HttpResponse 对象包含发送到客户端的响应,包括所有呈现的输出和 Cookie。

HttpApplication对象分配给请求

初始化所有核心应用程序对象之后,将通过创建 HttpApplication 类的实例启动应用程序。如果应用程序具有 Global.asax 文件,则 ASP.NET 会创建 Global.asax 类(从 HttpApplication 类派生)的一个实例,并使用该派生类表示应用程序。

Note注意

第一次在应用程序中请求 ASP.NET 页或进程时,将创建 HttpApplication 的一个新实例。不过,为了尽可能提高性能,可对多个请求重复使用 HttpApplication 实例。

创建 HttpApplication 的实例时,将同时创建所有已配置的模块。例如,如果将应用程序这样配置,ASP.NET 就会创建一个SessionStateModule 模块。创建了所有已配置的模块之后,将调用HttpApplication 类的 Init 方法。

下面的关系图说明了这种关系:

应用程序环境图形

HttpApplication管线处理请求。

在处理该请求时将由 HttpApplication 类执行以下事件。希望扩展 HttpApplication 类的开发人员尤其需要注意这些事件。

  1. 对请求进行验证,将检查浏览器发送的信息,并确定其是否包含潜在恶意标记。有关更多信息,请参见 ValidateRequest 和脚本侵入概述

  2. 如果已在 Web.config 文件的 UrlMappingsSection 节中配置了任何 URL,则执行 URL 映射。

  3. 引发 BeginRequest 事件。

  4. 引发 AuthenticateRequest 事件。

  5. 引发 PostAuthenticateRequest 事件。

  6. 引发 AuthorizeRequest 事件。

  7. 引发 PostAuthorizeRequest 事件。

  8. 引发 ResolveRequestCache 事件。

  9. 引发 PostResolveRequestCache 事件。

  10. 根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理。如果该请求针对从 Page 类派生的对象(页),并且需要对该页进行编译,则 ASP.NET 会在创建该页的实例之前对其进行编译。

  11. 引发 PostMapRequestHandler 事件。

  12. 引发 AcquireRequestState 事件。

  13. 引发 PostAcquireRequestState 事件。

  14. 引发 PreRequestHandlerExecute 事件。

  15. 为该请求调用合适的 IHttpHandler 类的 ProcessRequest 方法(或异步版 BeginProcessRequest)。例如,如果该请求针对某页,则当前的页实例将处理该请求。

  16. 引发 PostRequestHandlerExecute 事件。

  17. 引发 ReleaseRequestState 事件。

  18. 引发 PostReleaseRequestState 事件。

  19. 如果定义了 Filter 属性,则执行响应筛选。

  20. 引发 UpdateRequestCache 事件。

  21. 引发 PostUpdateRequestCache 事件。

  22. 引发 EndRequest 事件。

其中HttpApplication管线处理请求事件详解如下:

1、ValidatePathExecutionStep负责对请求的路径进行安全检查,禁止非法路径访问。深入到该类的Execute方法我们发现,该类直接调用HttpContext.ValidatePath方法,而后一个方法在检测到非法路径时,直接抛出HttpException。

2、UrlMappingsExecutionStep负责对Web.Config中设置的<urlMappings>配置节进行应用,将某一url映射为一个新的url地址。深入该类的Execute方法我们发现,该类在检测到需要url映射时,调用HttpContext.RewritePath方法实现地址重写。

3、引发BeginRequest事件:该步骤没啥好说的,就是通知开始处理请求了。然而,我在查阅有哪些Module订阅该事件时,发现该事件被UrlMappingsModule订阅,而该Module处理的事情跟UrlMappingsExecutionStep一模一样。我很纳闷,这不多此一举吗?

4、引发AuthenticateRequest、DefaultAuthenticateRequest、PostAuthenticateRequest事件负责对用户的身份进行验证。说白了,就是确定当前请求者的身份,从代码层次上说,就是设置好HttpContext的User属性。

      a) AuthenticateRequest事件根据web.config的配置,可被FormsAuthenticationModule、PassportAuthenticationModule、WindowsAuthenticationModule订阅,这3者分别对应处理不同的身份验证方式。

      b) DefaultAuthenticateRequest事件是个internal的事件,可被DefaultAuthenticationModule订阅,该module在AuthenticateRequest事件中无法确定用户身份(即没有设置HttpContext.User属性)时,给出一个默认的用户身份,从而确保在管线的后续处理中,HttpContext.User总是非空的。然而,查看该Module的源代码后,发现,该Module在IIS7.0的集成管线模式下时,并不订阅DefaultAuthenticationRequest事件,而是订阅PostAuthenticateRequest事件。估计是在IIS7.0的集成管线模式下时,该Module必须在某一订阅PostAuthenticateRequest的Module之后执行,从而打的一个补丁吧。

      c) PostAuthenticateRequest事件可被AnonymousIdentificationModule与RoleManagerModule订阅。AnonymousIdentificationModule管理应用程序的匿名标识符,可使应用程序进行匿名访问,并保持Session的连贯性。RoleManagerModule可确定用户的身份。

5、引发AuthorizeRequest、PostAuthorizeRequest事件:在经过第4个步骤确定用户身份后,接下来就是授权的问题了。即判断用户是否具有权限访问所请求的资源。在通过该步骤后,就确认用户具有对请求资源访问的权限。

      a) AuthorizeRequest事件可被FileAuthorizationModule、UrlAuthorizationModule订阅,分别用于判断用户是否具有访问指定File或Url的权限。

      b) PostAuthorizeRequest事件默认不被任何Module订阅。

6、引发ResolveRequestCache、PostResolveRequestCache事件:该步骤即是输出缓存发挥作用的时候。如果用户请求的资源可由输出缓存直接提供,则跳过后续步骤,从而起到加快处理速度,提高性能的作用。

      a) ResolveRequestChche事件可被OutputCacheModule订阅,该Module提供输出缓存的功能。

      b) PostResolveRequestCache事件默认不被任何Module订阅。

7、MapHandlerExecutionStep、引发PostMapRequestHandler事件:当无法由输出缓存进行处理时,则进入了正规的处理过程中。而正规处理的第一步 ,就是根据配置获取IHttpHandler的实例对象,从代码层次上说,就是设置好HttpContext.Handler属性。该Handler将具体根据上下文信息生成具体的Html等代码。

      最最常见的Handler对象就是Page类及其各个子类,也就是我们编写的每一个aspx页面,都是一个具体的Handler,MapHandlerExecutionStep类将根据用户访问各个aspx页面的url从而确定具体的page子类。

      另外,还有HttpForbiddenHandler,当用户访问App_Code、App_Data等目录中的资源时,MapHandlerExecutionStep就返回HttpForbiddenHandelr,该Handler将仅仅告诉用户该资源无法访问等信息,从而起到保护特定资源的作用。

      还有TraceHandler,该Handler当且仅当用户访问根目录下的"trace.axd"资源时返回,从而实现Asp.net跟踪的功能。

      用户可以注册更多的自定义Handler,已满足更多的特定的需求。

      在IIS7.0的集成管线模式下,还将引发MapRequestHandler事件。

      PostMapRequestHandler事件默认不被任何Module订阅。

8、引发AcquireRequestState、PostAcquireRequestState事件负责获取用户的会话状态、个性化数据等信息。从代码层次上说,就是设置好HttpContext.Session与HttpContext.Profile属性。

      a) AcquireRequestState事件可被SessionStateModule、ProfileModule订阅,这两个Module分别提供Session与Profile功能。

      b) PostAcquireRequestState事件默认不被任何Module订阅。

9、引发PreRequestHandlerExecute事件、CallHandlerExecutionStep、PostRequestHandlerExecute事件:至此,一切准备就绪,该执行第7部中获取的Handler的ProcessRequest的时候了。如果Handler为Page类或其子类,则该方法将引发Page的生命周期。

      a) PreRequestHandlerExecute与PostRequestHandlerExecute事件默认不被任何Module订阅。

      b) CallHandlerExecutionStep最主要的工作就是调用Handler的PrecessRequest方法,该方法负责生成Html等代码,并将这些代码写入HttpContext的Response中。因此,该步骤是最重要的一个步骤,也是最需要开发人员去定制的步骤,事实上,我们开发Asp.net应用程序,绝大部分就是在定制这一步的步骤,我们写出的一个又一个的不同的aspx页面,实际上就是一个又一个不同的Handler,而这些一个又一个不同的Handler,就生成了一个又一个不同的html页面。经过此步骤之后,Html等代码就基本完成了(为什么说基本完成了?是因为后续还有一个步骤将可能改变html代码,请看第11个步骤),后续的步骤,主要是一些收尾的工作。

10、引发ReleaseRequestState、PostReleaseRequestState事件:对应于第8个步骤,该步骤负责将用户的会话状态等信息保存起来,以供下次处理用户请求时所用。

      a) ReleaseRequestState事件可被SessionStateModule订阅,该Module在该步骤中保存可能被第9步骤所改变的会话状态。该事件并不被ProfileModule订阅,因为ProfileModule另外订阅EndRequest事件,在该事件中完成保存Profile数据的工作。

      b) PostReleaseRequestState事件默认不被任何Module订阅。

11、CallFilterExecutionStep:该步骤将执行HttpContext.Response.FilterOutput方法,将对已经写入Response的Html代码进行最后的过滤。而所谓过滤,实际上也就是改变html的一个过程,比如将html全部改为小写或全部改为大写,就是某一种形式的过滤。当然,你也可以说要过滤掉前面100个字符等,虽然通常这没有什么意义。要设置过滤器,请参考Response.Filter属性。

12、引发UpdateRequestCache、PostUpdateRequestCache事件:对应于第6个步骤,该步骤将生成的Html代码保存到输出缓存中,以供下次处理用户请求时所用。

      a) UpdateRequestCache事件可被OutputCacheModule订阅。

      b) PostUpdateRequestCache事件默认不被任何Module订阅。

13、引发EndRequest事件:对应于第3个步骤,该步骤通知请求已经处理完毕。该步骤有个特别之处,就在于前面那些步骤不一定在每次处理请求时都发生,而该步骤却必然发生,因此,该步骤也就成了很多Module处理扫尾等工作的订阅事件。ProfileModule、FormsAuthenticationModule、PassportAuthenticationModule、RoleManagerModule、SessionStateModule都订阅该事件,而各个Module订阅该事件所做的工作却又各不相同。

      a) ProfileModule订阅该事件是为了完成对Profile数据的保存。

      b) FormsAuthenticationModule订阅该事件是为了处理当用户未登录时将用户重定向到指定的Login页面的工作。

      c) PassportAuthenticationModule订阅该事件是为了处理当用户未登陆时将用户重定向到指定Passport地址的工作。

      d) RoleManagerModule订阅该事件的作用我还不大清楚,等待高手的补充或我的继续探索中。

      e) SessionStateModule订阅该事件的作用我也还不大清楚,等待高手的补充或我的继续探索中。

生命周期事件和 Global.asax 文件

在应用程序的生命周期期间,应用程序会引发可处理的事件并调用可重写的特定方法。若要处理应用程序事件或方法,可以在应用程序根目录中创建一个名为 Global.asax 的文件。

如果创建了 Global.asax 文件,ASP.NET 会将其编译为从 HttpApplication 类派生的类,然后使用该派生类表示应用程序。

HttpApplication 进程的一个实例每次只处理一个请求。由于在访问应用程序类中的非静态成员时不需要将其锁定,这样可以简化应用程序的事件处理过程。这样还可以将特定于请求的数据存储在应用程序类的非静态成员中。例如,可以在 Global.asax 文件中定义一个属性,然后为该属性赋一个特定于请求的值。

通过使用命名约定 Application_event(如 Application_BeginRequest),ASP.NET 可在 Global.asax 文件中将应用程序事件自动绑定到处理程序。这与将 ASP.NET 页方法自动绑定到事件(如页的 Page_Load 事件)的方法类似。有关详细信息,请参见 ASP.NET 页生命周期概述

Application_Start 和 Application_End 方法是不表示 HttpApplication 事件的特殊方法。在应用程序域的生命周期期间,ASP.NET 仅调用这些方法一次,而不是对每个 HttpApplication 实例都调用一次。

下表列出在应用程序生命周期期间使用的一些事件和方法。实际远不止列出的这些事件,但这些事件是最常用的。

事件或方法说明

Application_Start

请求 ASP.NET 应用程序中第一个资源(如页)时调用。在应用程序的生命周期期间仅调用一次 Application_Start 方法。可以使用此方法执行启动任务,如将数据加载到缓存中以及初始化静态值。

在应用程序启动期间应仅设置静态数据。由于实例数据仅可由创建的 HttpApplication 类的第一个实例使用,所以请勿设置任何实例数据。

Application_ event

在应用程序生命周期中的适当时候引发,请参见本主题前面的应用程序生命周期表中列出的内容。

Application_Error 可在应用程序生命周期的任何阶段引发。

由于请求会短路,因此 Application_EndRequest 是唯一能保证每次请求时都会引发的事件。例如,如果有两个模块处理Application_BeginRequest 事件,第一个模块引发一个异常,则不会为第二个模块调用 Application_BeginRequest 事件。但是,会始终调用 Application_EndRequest 方法使应用程序清理资源。

HttpApplication.Init

在创建了所有模块之后,对 HttpApplication 类的每个实例都调用一次。

Dispose

在销毁应用程序实例之前调用。可使用此方法手动释放任何非托管资源。有关更多信息,请参见清理非托管资源

Application_End

在卸载应用程序之前对每个应用程序生命周期调用一次。

原文地址:https://www.cnblogs.com/bianlan/p/3098734.html