ASP.NET管道
以IIS6.0为例,它在工作进程w3wp.exe中会利用aspnet_isapi.dll加载.NET运行时(如果.NET运行时尚未加载)。IIS6.0引入了应用程序进程池的概念,一个工作进程对应着一个应用程序池。一个应用程序进程池可以承载一个或者多个Web应用,每个Web应用映射到一个IIS虚拟目录。与IIS5.x一样,每一个Web应用运行在各自的应用程序域中。
如果HTTP.SYS接收到的HTTP请求是对该Web应用的第一次访问,在成功加载运行时后,IIS会通过AppDomainFactory为该Web应用创建一个应用程序域。随后一个特殊的运行时IsapiRuntime被加载。IsapiRuntime定义在程序集System.Web.dll中,对应的命名空间为“System.Web.Hosting”,被加载的IsapiRuntime会接管该HTTP请求。
接管HTTP请求的IsapiRuntime会首先创建一个IsapiWorkerRequest对象来封装当前的HTTP请求,随后将此对象传递给ASP.NET运行时HttpRuntime。从此时起,HTTP请求正式进入了ASP.NET管道。HttpRuntime会根据IsapiWorkerRequest对象创建用于表示当前HTTP请求的上下文(Context)对象HttpContext。
随着HttpContext的创建,HttpRuntime会利用HttpApplicationFactory创建新的或获取现有的HttpApplication对象。实际上ASP.NET维护着一个HttpApplication对象池,HttpApplicationFactory从池中选取可用的HttpApplication用于处理HTTP请求,处理完毕后将其释放到对象池中。HttpApplication负责处理当前的HTTP请求。
在HttpApplication初始化过程中,ASP.NET会根据配置文件加载并初始化注册的HttpModule对象。对于HttpApplication来说,在它处理HTTP请求的不同阶段会触发不同的事件(Event),而HttpModule的意义在于通过注册HttpApplication的相应事件,将需要的操作注入整个HTTP请求的处理流程。ASP.NET的很多功能都是通过相应的HttpModule实现的。
最终完成对HTTP请求的处理实现在HttpHandler中,不同的资源类型对应着不同类型的HttpHandler。
1.HttpApplication
HttpApplication是整个ASP.NET基础架构的核心,他负责处理分发给他的HTTP请求。由于一个HttpApplication对象在某个时刻只能处理一个请求,只有完成对某个请求的处理后才能用于后续请求的处理,所以ASP.NET采用对象池的机制来创建或获取HttpApplication对象。
当第一个请求抵达时,ASP.NET会一次创建多个HttpApplication对象,并将其置于池中,然后选择其中一个对象来处理请求。处理完毕后,HttpApplication对象不会被回收,而是释放到池中。对于后续的请求,空闲的HttpApplication对象会从池最中取出。如果池中所有的HttpApplication对象都处于繁忙状态,在没有超出HttpApplication池最大容量的情况下,ASP.NET会创建新的HttpApplication对象,否则将请求放入队列等待现有HttpApplication的释放。
HttpApplication处理请求的整个生命周期是一个相对复杂的过程,在该过程的不同阶段会触发相应的事件。我们可以注册相应的事件,将处理逻辑注入到HttpApplication处理请求的某个阶段。下表按照实现的先后顺序列出了HttpApplication在处理每一个请求时触发的事件名称。
HttpApplication事件列表
名称 |
描述 |
BeginRequest |
HTTP管道开始处理请求时,会触发BeginRequest事件。 |
AuthenticateRuest, PostAuthenticateRequest |
ASP.NET先后触发这两个事件,使安全模块对请求进行身份验证。 |
AuthorizeRequest, PostAuthorizeRequest |
ASP.NET先后触发这两个事件,使安全模块对请求进行授权。 |
ResolveRequestCache, PostResolveRequestCache |
ASP.NET先后触发这两个事件,以使缓存模块利用缓存的内容对请求直接进行响应(缓存模块可以将响应内容进行缓存,对于后续的请求,直接将缓存的内容返回,从而提高响应能力) |
PostMapRequestHandler |
对于访问不同的资源类型,ASP.NET具有不同的HttpHandler对其进行处理。对于每个请求,ASP.NET会通过扩展名选择匹配相应的HttpHandler类型,匹配成功后,该事件触发 |
AcquireRequestState, PostAcquireRequestState |
ASP.NET先后触发这两个事件,使状态管理模块获取基于当前请求相应的状态,如SessionState |
PreRequestHandlerExecute, PostPreRequestHandlerExecute |
ASP.NET最终通过与请求资源相对应的HttpHandler实现对请求的处理,在实行HttpHandler前后,这两个事件被先后触发 |
ReleaseRequestState, PostReleaseRequestState |
ASP.NET先后触发这两个事件,使状态管理模块释放基于当前请求相应的状态 |
UpdateRequestCache, PostUpdateRequestCache |
ASP.NET先后触发这两个事件,以使缓存模块将HttpHandler处理请求得到的内容得以保存到输出缓存中。 |
LogRequest, PostLogRequest |
ASP.NET先后触发这两个事件为当前请求进行日志记录 |
EndRequest |
整个请求处理完成之后,EndRequest事件被触发 |
对于一个ASP.NET应用来说,HTTPApplication派生于Global.asax文件,我们可以通过创建Global.asax文件对HttpApplication的请求处理行为进行定制。Global.asax采用一种很直接的方式实现了这样的功能,这种方式不是我们常用的方法重写或事件注册,而是直接采用方法名匹配。在Global.asax中。我们按照”Application_{Event Name}”这样的方法命名规则进行事件注册,比如Application_BeginRequestf方法处理HttpApplication的BeginRequest事件。
2.HttpModule
ASP.NET拥有一个具有高度可扩展性的引擎,并且能够处理对于不同资源类型的请求。HttpModule功不可没。
当请求转入ASP.NET管道时,最终负责处理该请求的是与请求资源类型相匹配的HttpHandler对象,但是在HttpHandler正式工作之前ASP.NET会先加载并初始化所有配置的HttpModule对象。HttpModule在初始化过程中,会将一些回调操作注册到HttpApplication相应的事件中,在HttpApplication请求处理生命周期中的某个阶段,相应的事件会被触发,通过HttpModule注册的事件处理程序也得以执行。
所有的HttpModule都实现了具有如下定义的System.Web.IHttpModule接口,其Init方法实现了针对自身的初始化。该方法接收一个HttpApplication对象,有了这个对象,事件注册就很容易了。
public interface IHttpModule { void Dispose(); void Init(HttpApplication context); }
ASP.NET提供的很多基础功能都是通过相应的HttpModule实现的,下面列出了一些典型的HttpModule,除了这些系统定义的HttpModule之外,我们还可以自定义HttpModule,通过Web.config可以很容易地将其注册到Web应用中。
(1)OutputCacheModule:实现了输出缓存的功能。
(2)SessionStateModule:在无状态的HTTP协议上实现了基于会话的状态保持。
(3)WindowAuthenticationModule + FormsAuthenticationModule + PassportAuthenticationModule:实现了Window、Forms和Passport这三种典型的身份认证方式。
(4)UrlAuthorizationModule + FileAuthorizationModule:实现了基于URI和文件ACL的授权。
3.HttpHandler
对于不同资源类型的请求,ASP.NET会加载不同的Handler来处理。所有的HttpHandler都实现了具有如下定义的接口System.Web.IHttpHandler,定义其中的方法ProcessRequest提供了处理请求的实现。另一个异步版本的HttpHandler的IHttpAsyncHandler接口继承自IHttpHandler,它通过调用BeginProcessRequest/EndProcessRequest方法以异步的方式处理请求。
public interface IHttpHandler { void ProcessRequest(HttpContext context); bool IsReusable{get;} } public interface IHttpAsyncHandler : IHttpHandler { IAsyncRequest BeginProcessRequest(HttpContext context,AsyncCallback ch,object extraData); void EndProcessRequest(IAsyncRequest result); }
某些HttpHandler具有一个与之相关的HttpHandlerFactory,定义其中的方法GetHandler用于创建新的HttpHandler或者获取已经存在的HttpHandler。
public interface IHttpHandlerFactory { IHttpHandler GetHandler(HttpContext context,String requestType,string url,string pathTranslated); void ReleaseHandler(IHttpHandler handler); }
HttpHandler和HttpHandlerFactory的类型都是配置在Web.config来使用的。
除了通过配置建立起HttpHandler类型与请求路径之间的映射关系之外,我们还可以调用当前HttpContext具有如下定义的RemapHandler方法将一个HttpHandler对象映射到当前HTTP请求。如果不曾通过调用该方法进行HttpHandler的显示映射,或者调用该方法时传入的参数为Null,真正的HttpHandler对象的映射发生在HttpApplication的PostMapRequestHandler触发之前,默认进行HttpHandler的依据就是Web.config的配置。
public sealed class HttpContext { public void RemapHandler(IHttpHandler handler) }
换句话说,在调用当前HttpContext的RemapHandler方法时指定一个具体的HttpHandler对象,是为了让ASP.NET直接跳过默认的HttpHandler映射操作。由于默认的HttpHandler映射发生在HttpApplication的PostMapRequestHandler事件触发之前,所以只有在这之前调用RemapHandler方法才有意义。