在ASP.NET编程模型中,一个来自客户端的请求要经过一个称为管线的处理过程。
在整个处理请求中,相对于其它对象来说,HttpHandler的处理算得上是整个过程的核心部分。
由于HttpHandler的重要地位,我前面已经有二篇博客对它过一些使用上的介绍。
【用Asp.net写自己的服务框架】
中谈到了它的一般使用方法。
【细说ASP.NET的各种异步操作】
又详细地介绍了异步HttpHandler的使用方式。
今天的博客将着重介绍HttpHandler的配置,创建以及重用过程,还将涉及HttpHandlerFactory的内容。
回顾HttpHandler
HttpHandler其实是一类统称:泛指实现了IHttpHandler接口的一些类型,这些类型有一个共同的功能,那就是可以用来处理HTTP请求。
IHttpHandler的接口定义如下:
// 定义 ASP.NET 为使用自定义 HTTP 处理程序同步处理 HTTP Web 请求而实现的协定。
public interface IHttpHandler
{
// 获取一个值,该值指示其他请求是否可以使用 System.Web.IHttpHandler 实例。
//
// 返回结果:
// 如果 System.Web.IHttpHandler 实例可再次使用,则为 true;否则为 false。
bool IsReusable { get; }
// 通过实现 System.Web.IHttpHandler 接口的自定义 HttpHandler 启用 HTTP Web 请求的处理。
void ProcessRequest(HttpContext context);
}
有关HttpHandler的各类用法,可参考我的博客【用Asp.net写自己的服务框架】,
本文将不再重复说明了。它还有一个异步版本:
// 摘要:
// 定义 HTTP 异步处理程序对象必须实现的协定。
public interface IHttpAsyncHandler : IHttpHandler
{
// 摘要:
// 启动对 HTTP 处理程序的异步调用。
//
// 参数:
// context:
// 一个 System.Web.HttpContext 对象,该对象提供对用于向 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session
// 和 Server)的引用。
//
// extraData:
// 处理该请求所需的所有额外数据。
//
// cb:
// 异步方法调用完成时要调用的 System.AsyncCallback。如果 cb 为 null,则不调用委托。
//
// 返回结果:
// 包含有关进程状态信息的 System.IAsyncResult。
IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
//
// 摘要:
// 进程结束时提供异步处理 End 方法。
//
// 参数:
// result:
// 包含有关进程状态信息的 System.IAsyncResult。
void EndProcessRequest(IAsyncResult result);
}
IHttpAsyncHandler接口的二个方法该如何使用,可参考我的博客【细说ASP.NET的各种异步操作】,
本文也将不再重复讲解。
如果我们创建了一个自定义的HttpHandler,那么为了能让它处理某些HTTP请求,我们还需将它注册到web.config中,就像下面这样:
<httpHandlers>
<add path="*.fish" verb="*" validate="true" type="MySimpleServiceFramework.AjaxServiceHandler"/>
</httpHandlers>
虽然我以前的博客中也曾多次涉及到HttpHandler,但感觉还是没有把它完整地说清楚,今天的博客将继续以前的话题,因为我认为HttpHandler实在是太重要了。
HttpHandler的映射过程
在博客【用Asp.net写自己的服务框架】中,
我从MSDN中摘选了一些ASP.NET管线事件,在这些事件中,第10个事件【根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理】
就是本文要介绍的重点事件。这个事件也是HttpHandler创建的地方。
由于IIS6,7在管线事件触发机制上的有一定的差别,本文将以ASP.NET 2.0以及IIS6的运行方式来介绍这个过程,
IIS7的集成模式下只是触发机制不同,但事件的绝大部分是一样的。
管线事件由HttpApplication控制,由MapHandlerExecutionStep负责封装这个事件的执行过程:
internal class MapHandlerExecutionStep : HttpApplication.IExecutionStep
{
private HttpApplication _application;
void HttpApplication.IExecutionStep.Execute()
{
HttpContext context = this._application.Context;
HttpRequest request = context.Request;
//我删除了一些与本文无关的调用代码
context.Handler = this._application.MapHttpHandler(
context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);
}
Execute()最终调用了MapHttpHandler()方法,这个映射过程是由HttpApplication实现的。
MapHttpHandler方法的实现如下:注意代码中我加入的注释。
/// <summary>
/// 根据虚拟路径映射到具体的HttpHandler对象
/// </summary>
/// <param name="context">HttpContext的实例</param>
/// <param name="requestType">GET, POST ...</param>
/// <param name="path">一个虚拟路径映射的字符串: /abc.aspx</param>
/// <param name="pathTranslated">要请求的物理文件的路径</param>
/// <param name="useAppConfig"></param>
/// <returns>具体的HttpHandler对象</returns>
internal IHttpHandler MapHttpHandler(HttpContext context, string requestType,
VirtualPath path, string pathTranslated, bool useAppConfig)
{
// 说明:这段代码是我精简后的代码。为了便于理解,我去掉了一些与请求无关的代码行。
IHttpHandler handler = (context.ServerExecuteDepth == 0) context.RemapHandlerInstance : null;
//用于将当前线程的运行帐号切换为web.config指定的帐号
using( new ApplicationImpersonationContext() ) {
// 如果之前调用过context.RemapHandler(),则直接返回那个HttpHandler
// 把这个判断放在这里似乎没有意义,应该放在using前面,那样岂不是更快吗?【fish li个人意见】
if( handler != null )
return handler;
// 到 <httpHandlers> 配置中查找一个能与requestType以及path匹配的配置项
HttpHandlerAction mapping = this.GetHandlerMapping(context, requestType, path, useAppConfig);
if( mapping == null )
throw new HttpException("Http_handler_not_found_for_request_type");
// 获取IHttpHandlerFactory对象,这是比较重要的调用。
IHttpHandlerFactory factory = this.GetFactory(mapping);
// 尝试转换成IHttpHandlerFactory2对象。设计二个IHttpHandlerFactory的原因与参数的可见类型有关。
IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2;
// 调用IHttpHandlerFactory对象的GetHandler方法获取具体的HttpHandler对象
if( factory2 != null )
handler = factory2.GetHandler(context, requestType, path, pathTranslated);
else
handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated);
// 将这二个对象都添加到【临时】回收列表。在管线处理的结尾处,会调用HandlerWithFactory.Recycle()
// 进而会调用factory.ReleaseHandler(),最后会丢掉_handlerRecycleList的引用。
// 因此factory的重用与这里无关,它只可能会影响handler
this._handlerRecycleList.Add(new HandlerWithFactory(handler, factory));
}
return handler;
}
今天的代码将着重分析这段代码,以揭示HttpHandler的映射过程。
在这段代码中,有3个主要的调用过程:
1. this.GetHandlerMapping(context, requestType, path, useAppConfig)
2. this.GetFactory(mapping)
3. factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated)
后面将着重分析这三个调用。
HttpContext.RemapHandler()
在MapHttpHandler()方法的开始处,有这么一段代码,不知您注意没有?
IHttpHandler handler = (context.ServerExecuteDepth == 0) context.RemapHandlerInstance : null;
if( handler != null )
return handler;
这段代码是什么意思呢?
为了能让您较为简单地理解这段代码的意义,请看我准备的一个示例:
我创建了一个TestRemapHandler.ashx文件:
<%@ WebHandler Language="C#" Class="TestRemapHandler" %>
using System;
using System.Web;
public class TestRemapHandler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
context.Response.Write("Hello TestRemapHandler");
}
public bool IsReusable {
get {
return false;
}
}
}
至于这个文件在运行时能输出什么,我想我就不用截图了。
接下来,我再创建一个Global.asax文件,并写了一个事件处理方法:
//protected void Application_PostResolveRequestCache(object sender, EventArgs e)
//{
// HttpApplication app = (HttpApplication)sender;
// if( app.Request.FilePath.EndsWith(".ashx", StringComparison.OrdinalIgnoreCase) )
// app.Context.RemapHandler(new MyTestHandler());
/
上一篇: