应用程序的初始化
一旦请求上下文被创建,HttpRuntime类便会建立处理请求的ASP.NET应用程序对象。ASP.NET应用程序由许多HttpApplication类的实例组成。HttpApplication是根据global.asax文件派生的对象,能够处理转到特定虚拟文件夹的所有HTTP请求。
运行中的ASP.NET应用程序完全由其虚拟文件夹表示的,且global.asax文件是可选的。虚拟文件夹的名称是一种键,HTTP运行库通过它来确定应该由哪个应用程序来处理传入的请求。
请求会被分配给某个HttpApplication对象,此后便由这个选定的对象来负责管理请求的整个生存期。只有在请求处理完成后,HttpApplication对象才能被重新使用。如果没有可用的HttpApplication对象,则将创建新的HttpApplication对象,并被放入池中。
虽然HttpApplication类提供了一个公共的构造函数,但用户应用程序不能直接创建该类的实例。ASP.NET运行库的甚础结构会为我们完成这项工作。下表列出了该类的属性:
应用程序模块
Modules属性能返回应用程序级组件的集合,每个组件会提供其特有的服务。HTTP模块组件是实现IHttpModule接口的类,我们可以将托管的模块视同于ISAPI筛选器,它们都是某种拦截程序,能够修改当前被处理请求的整个上下文。.NET Framework定义了许多标准的模块(见下表),我们也可以自定义模块。
默认模块的列表定义在machine.config文件中。machine.config文件中列出的模块对应所有应用程序都可用。通过创建合适的web.config,我们还可以添加应用程序的模块列表。
HttpApplication类的方法
可以将HttpApplication的方法分成两类:操作方法和事件处理程序管理方法。下表列出了HttpApplication类的操作方法:
注意,Init和Dispose方法与人们熟知的Application_Start和Application_End事件处理程序有很大差异。对于每个针对Web应用程序的请求,Init都会执行,而Application_Start只在Web应用程序的生存期中执行一次。Init会指示已初始化的Application类的新实例处理传入的请求,而Application_Start会启动Web应用程序并创建第一个HttpApplication类的实例来处理首个请求。类似的,Dispose用于通知请求处理的终止,但并不一定是应用程序的结束。Application_End只会在应用程序关闭时引发,因而只引发一次。
所有在Init方法中创建的资源的生存期仅限于当前请求的执行期间。在Init中创建的所有资源,最终应被释放,最迟在Dispose中进行。如果需要保存数据,则应借助于应用程序中的其他对象或会话状态。
除上表中的方法,HttpApplication还有几个用于注册应用程序级事件异步处理程序的方法。用户应用程序不会用到这些方法,只有HTTP模块用它们来注册请求的执行链中生成的事件。
HttpApplication类的事件
下表简单描述了HttpApplication类的事件模型,HTTP模块和用户应用程序都可以监听并处理这些事件:
若要以异步方式处理这些事件,应用程序需要使用带有AddOnXXXAsync命名模式的方法,其中XXX代表事件名称。若要以同步方式注册这些事件,则需要在global.asax中定义事件处理程序,并遵循以下签名规则:
{
// Do something here
}
该表中的所有事件都与数据无关,因此,使用下面的语法亦可:
{
// Do something here
}
除上表列出的事件外,还可以在global.asax中处理Application_Start和Application_End。当ASP.NET在应用程序的生存期最开始处引发BeginRequest前,它会先引发Application_Start。EndRequest会在每个应用程序请求处理完毕后引发。Application_End会在应用程序执行结束时引发,与请求的上下文无绝对关系。
应用程序事件会按以下顺序引发:
1. BeginRequest:ASP.NET HTTP管道开始处理请求。该事件会在Application_Start之后引发。
2. AuthenticateRequest:即将对发出请求的用户进行身份验证。ASP.NET内部所有的身份验证模块都订阅该事件,并试图生成用户标识。如果没能生成已验证用户标识的身份验证模块,则会调用内部默认的身份验证模块,为这个验证用户生成一个标识。由于存在这种一致性,因而不必担心出现null标识的情况。
3. PostAuthenticateRequest:发出请求的用户已被验证,所有信息会存储在HttpContext的User属性中。
4. AuthorizeRequest:即将进行请求授权,该事件一般由应用程序的代码处理,基于业务逻辑或应用程序需求来进行自定义的授权过程。
5. PostAuthorizeRequest:请求已被授权。
6. ResolveRequestCache:ASP.NET运行库要检查是否返回之前的缓存页面来处理请求。如果发现有效的缓存页面,则由缓存的内容来处理请求,从而缩短请求的过程,直接调用注册EndRequest事件的处理程序。
7. PostResolveRequestCache:请求不能由缓存的内容进行处理,该流程继续进行。此时,与请求的URL对应的HTTP处理程序会被创建。如果请求的资源是.aspx页面,则创建页面类的实例。
8. PostMapRequestHandler:该事件会在与请求的URL对应的HTTP处理程序成功创建时引发。
9. AcquireRequestState:订阅该事件的模块要获取请求的状态信息。这涉及到很多必要条件:处理程序必须会执行其自身的逻辑,并为客户端生成输出。
10. PostAcquireRequestState:状态信息(如Application和Session)已获得。
11. PreRequestHandlerExecute:该事件恰好在给定请求的处理程序执行前引发。该处理程序会执行其自身的逻辑,并为客户端生成输出。
12. PostRequestHandlerExecute:该事件会在处理程序生成响应文本后引发。
13. ReleaseRequestState:该事件会在处理程序释放其状态信息并准备关闭时引发。会话状态模块会在该事件引发时更新失效的会话状态。
14. PostReleaseRequestState:会话状态已被保存(若页面的执行被修改)。所有相关的响应筛选工作此时已结束。
15. UpdateRequestCache:ASP.NET运行库决定是否缓存已生成的输出(此时还会由注册的模板进行筛选),以便相同的传入请求可以重用它。
16. PostUpdateRequestCache:如果启用了缓存,则页面已被保存到输出缓存中。
17. EndRequest:该事件是HTTP管道的最后一步,控制权会交还给HttpRuntime对象,该对象负责将响应发送给客户端。此时,输出文本尚未被发送。
还有一对事件会在请求处理期间被引发,但时机并不确定。这两个事件为PreSendRequestHeaders和PreSendRequestContent。
PreSendRequestHeaders用于通知负责当前请求的HttpApplication对象将HTTP标头发送给客户端。该事件一般会在EndRequest之后引发,但并不总是这样。例如:如果缓冲功能被关闭,该事件会在某些内容即将发送给客户端时引发。
通过PreSendRequestContent事件,负责当前请求的HttpApplication对象会得知响应的主体内容即将发送。
还有一个不确定的应用程序事件Error。