ASP.NET 处理请求的过程
- inetinfo.exe:www 服务进程,IIS 服务 和 ASPNET_ISAPI.dll 都寄存在此进程中。
- ASPNET_ISAPI.dll:处理 .aspx 文件的 win32 组件。其实,IIS 服务器只能识别 .html 文件的,当发现被请求的文件是 .aspx 文件时,IIS 服务器将其交给 aspnet_isapi.dll 来处理。
- aspnet_wp.exe 进程:ASP.NET 框架进程,提供 .net 运行的托管环境,CLR (公共语言运行时)就是寄存在此进程中。
ASP.NET Framework 处理一个 Http Request 的流程:
HttpRequest → inetinfo.exe → ASPNET_ISAPI.dll → ASPNET_WP.exe → HttpRuntime → HttpApplication Factory → HttpApplication → HttpModule → HttpHandler Factory → HttpHandler → HttpHandler.ProcessRequest()
ASP.NET 请求处理过程是基于管道模型的,ASP.NET 把 http 请求依次传递给管道中各个 HttpModule,最终被 HttpHandler 处理,处理完成后,再次经过管道中的 HttpModule,把结果返回给客户端。注意:在 http 请求的处理过程中,只能调用一个 HttpHandler,但可以调用多个 HttpModule。
在每个 HttpModule 中都可以干预请求的处理过程。当请求到达 HttpModule 的时候,系统还没有对这个请求真正处理,但是我们可以在这个请求传递到处理中心(HttpHandler)之前附加一些其它信息,截获这个请求并作一些额外的工作,或者终止请求等。在 HttpHandler 处理完请求之后,我们也可以再在相应的 HttpModule 中把请求处理的结果进行再次加工后返回客户端。
HttpModule
HttpModule 是实现了 System.Web.IhttpModule 接口的类。IHttpModule 接口的声明:
public interface IHttpModule
{
// 给予 HTTP 模块在对象被垃圾收集之前执行清理的机会,一般无需编写代码。
void Dispose();
// 初始化时自动调用,可在这里向 HttpApplication 对象中的事件注册自己的事件处理程序
void Init(HttpApplication context);
}
HttpModule 可以向 System.Web.HttpApplication 对象注册下面一系列事件(事件触发顺序已排序):
- application_BeginRequest:当 ASP.NET 运行时接收到新的 HTTP 请求时触发。
- application_AuthenticateRequest:当 ASP.NET 运行时准备验证用户身份时触发。
- application_AuthorizeRequest:当 ASP.NET 运行时准备授权用户访问资源的时候触发。
- application_ResolveRequestCache:我们引发这个事件来决定是否可以使用从输出缓存返回的内容来结束请求。但这依赖于 Web 应用程序的输出缓存是怎样设置的。
- application_AcquireRequestState:当 ASP.NET 运行时准备好接收当前 HTTP 请求的会话状态的时候引发这个事件。
- application_PreRequestHandlerExecute:在 ASP.NET 开始执行 HTTP 请求的处理程序之前引发这个事件。在这个事件之后,ASP.NET 把该请求转发给适当的HTTP处理程序。
- application_PostRequestHandlerExecute:在 HTTP 处理程序结束执行的时候引发这个事件。
- application_ReleaseRequestState:当 ASP.NET 结束所有的请求处理程序执行的时候引发这个事件。
- application_EndRequest:把响应内容发送到客户端之前引发这个事件。
- application_PreSendRequestHeaders:在 ASP.NET 把 HTTP 响应头信息发送给客户端之前引发这个事件。在头信息到达客户端之前,这个事件允许我们改变它的内容。我们可以使用这个事件在头信息中添加cookie 和自定义数据。
- application_PreSendRequestContent:在 ASP.NET 把响应内容发送到客户端之前引发这个事件。这个事件允许我们在内容到达客户端之前改变响应内容。我们可以使用这个事件给页面输出添加用于所有页面的内容。例如通用菜单、头信息、脚信息或某些注释与标记。
其余,还有 Error 事件(在处理 HTTP 请求的过程中出现未处理的异常时触发),Deposed 事件(当 ASP.NET 完成 HTTP 请求的处理过程时触发),UpdateRequestCache 事件(当 ASP.NET 完成了当前的 HTTP 请求的处理,并且输出内容已经准备好添加给输出缓存时触发。这依赖于 Web 应用程序的输出缓存是如何设置的)。
HttpModule & HttpHandler 的生命周期图:
HttpModule 使用示例1
有的网站每一个页面都会弹出一个广告或在每个页面都以注释形式(<!-- -->)加入网站的版权信息。如果在每个页面教编写这样的 JS 代码,编写会很繁琐,更改和维护将非常困难。
HttpModule 是客户端发出请求到客户端接收到服务器响应之间的一段必经之路。我们完全可以在服务器处理完请求之后,并在向客户端发送响应文本之前这段时机,把这段注释文字添加到页面文本之后。这样,每一个页面请求都会被附加上这段注释文字。那么,这段代码究竟该在哪个事件里实现呢? PostRequestHandlerExecute 和 PreSendRequestContent 之间的任何一个事件都可以,但一般会在 EndRequest 事件里编写代码,具体实现步骤如下:
- 添加自定义的管道类,并实现 IHttpModule 接口
- 在 Init 事件中注册 EndRequest 事件,实现事件处理方法
- 在 Web.Conofig 中注册自定义 HttpModule
public class TestModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.EndRequest += context_EndRequest;
}
void context_EndRequest(object sender, EventArgs e)
{
((HttpApplication)sender).Response.Write("<!-- 公司版权 -->");
}
}
<httpModules>
<add name="TestModule" type="ASP.NET_Web_Form.Util.TestModule,ASP.NET_Web_Form.Util"/>
</httpModules>
HttpModule 使用示例2
在作登录时,登录成功后,一般要把用户名放在 Session 中保存,在其它每一个页面的 Page_Load 事件中都检查 Session 中是否存在用户名,如果不存在就说明用户未登录,就不让其访问其中的内容。但这种做法实在是太笨拙,几乎要在每一个页面中都加入检测 Session 的代码,导致难以开发和维护。下面我们看看如何使用 HttpModule 来减少我们的工作量。(如果每个页面都有共同的父类,例如 BasePage,那么在父类的 Load 事件中检测也是一样的只需写一次检测代码)
由于这里要用到 Session 中的内容,我们只能在 AcquireRequestState 和 PreRequestHandlerExecute 事件中编写代码,因为在 HttpModule 中只有这两事件中可以访问 Session。这里可以选择PreRequestHandlerExecute 事件编写代码,具体实现比较简单,就不贴代码了。
HttpHandler
HttpHandler 是 HTTP 请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在 HTTP 请求信息流中再次返回到 HttpModule 中。与 HttpModule 不同,一旦定义了自己的HttpHandler 类,那么它对系统的 HttpHandler 的关系将是“覆盖”。
在 web.config 中配置 HttpHandler 节时,你可以指定 HTTP 的动词(GET,POST),指定要处理的资源是什么,以及用什么 HttpHandler 来进行。
另外,如果要在 HttpHandler 中使用 Session,那必须把该 HttpHandler 实现 IRequiresSessionState 接口,IRequiresSessionState 接口是个空接口,它没有抽象方法,只是一个标记接口。
本文参考文献:http://www.cnblogs.com/yuanyuan/archive/2010/11/15/1877709.html