细说 HttpHandler 的映射过程
在ASP.NET编程模型中,一个来自客户端的请求要经过一个称为管线的处理过程。在整个处理请求中,相对于其它对象来说,HttpHandler的处理算得上是整个过程的核心部分。由于HttpHandler的重要地位,我前面已经有二篇博客对它过一些使用上的介绍。 【用Asp.net写自己的服务框架】 中谈到了它的一般使用方法。 【细说ASP.NET的各种异步操作】 又详细地介绍了异步HttpHandler的使用方式。
今天的博客将着重介绍HttpHandler的配置,创建以及重用过程,还将涉及HttpHandlerFactory的内容。
回到顶部回顾HttpHandlerHttpHandler其实是一类统称:泛指实现了IHttpHandler接口的一些类型,这些类型有一个共同的功能,那就是可以用来处理HTTP请求。 IHttpHandler的接口定义如下:
虽然我以前的博客中也曾多次涉及到HttpHandler,但感觉还是没有把它完整地说清楚,今天的博客将继续以前的话题,因为我认为HttpHandler实在是太重要了。
回到顶部HttpHandler的映射过程在博客【用Asp.net写自己的服务框架】中,我从MSDN中摘选了一些ASP.NET管线事件,在这些事件中,第10个事件【根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理】就是本文要介绍的重点事件。这个事件也是HttpHandler创建的地方。
由于IIS6,7在管线事件触发机制上的有一定的差别,本文将以ASP.NET 2.0以及IIS6的运行方式来介绍这个过程, IIS7的集成模式下只是触发机制不同,但事件的绝大部分是一样的。管线事件由HttpApplication控制,由MapHandlerExecutionStep负责封装这个事件的执行过程: 『点击此处展开』
至于这个文件在运行时能输出什么,我想我就不用截图了。
接下来,我再创建一个Global.asax文件,并写了一个事件处理方法: 『点击此处展开』现在,您可以想像一下当我再次访问TestRemapHandler.ashx时,会在浏览器中看到什么?
以下是我看到的结果:
从截图中,我们可以看出,服务端的输出并不是TestRemapHandler.ashx产生的,而是由MyTestHandler产生的。这也说明我调用Context.RemapHandler()确实影响了后面的MapHttpHandler过程。现在我们再回过头来再看一下前面那段代码:
我的示例网站中的web.config中的<httpHandlers>配置节定义如下:
那么,前面的示例代码的运行结果如下:
通过前面分析可知,代码中的HttpHandlers实际就是在运行时所能访问到的<httpHandlers>配置集合。而且,我们在网站中所指定的配置会放在集合的前面,ASP.NET的默认配置会在靠后的位置。接下来的FindMapping(requestType, path)的实现如下: 『点击此处展开』
从MSDN的解释中可以看出:verb,path都可以支持【通配符(*)】,并且可以使用逗号(,)来分隔多个预期项。 ASP.NET在实现通配符时,内部使用正则表达式的方式,具体过程可以自己去阅读相关代码。
下面,我将通过一个具体的配置项来分析这些配置参数:
1. path="Ajax*.*.aspx,Ajax*/*.aspx":表示可以接受【Ajax*.*.aspx】或者【Ajax*/*.aspx】这样的URL格式。
2. verb="*": 表示可以接受所有的HTTP调用,如:GET,POST 等等。
3. type="MySimpleServiceFramework.AjaxServiceHandler, MySimpleServiceFramework": 表示当某个请求匹配path,verb时,交给MySimpleServiceFramework程序集中的MySimpleServiceFramework.AjaxServiceHandler来处理请求。
4. validate="true":表示在第一次读取<httpHandlers>时,验证type的设置是否有效。如果此项设为false,则表示延迟验证(创建时)。默认值:true对于这4个属性,有2个比较重要:
1. type: type中的字符串必须能在运行时找到一个可创建的类型,且该类型必须实现IHttpHandler或者IHttpHandlerFactory接口。
2. path: 在很多资料以及技术书籍中,一般都设置为某类文件扩展名,如:*.xxx ,事实上,我们完全可以设置为一个URL模式,而且还可以设置多个URL模式。比如上面的配置示例中的path参数,具体在使用时,可以响应来自客户端的以下请求: 『点击此处展开』我想大家都知道aspx这个扩展名是ASP.NET可处理的扩展名,为什么我还可以使用呢?
其实这个问题的答案在我前面的截图以及FindMapping的实现代码中:由于我们指定的配置要优先于ASP.NET的默认配置,所以先有机会参与匹配,并能匹配成功。小结:在GetHandlerMapping()过程中,会根据请求的URL地址以及HTTP调用动作(GET,POST),返回一个在<httpHandlers>定义的配置项(HttpHandlerAction类型)。
回到顶部GetFactory()在GetHandlerMapping()返回一个HttpHandlerAction的配置项后, HttpApplication会调用this.GetFactory(mapping);获取一个IHttpHandlerFactory ,本小节将来分析这个过程。
首先,我们还是来看一下GetFactory的实现代码: 『点击此处展开』
有趣的是:PageHandlerFactory和SimpleHandlerFactory都实现了IHttpHandlerFactory2接口,因此,它们都可以根据要请求的路径创建一个IHttpHandler实例。
从ASP.NET的默认配置,我们也可以看到:type参数是可以设置为一个实现IHttpHandlerFactory接口的类型,而不一定要求是实现IHttpHandler接口的类型。
小结:HttpApplication在处理请求时,并不会直接创建一个IHttpHandler的实例,而是先获取一个IHttpHandlerFactory的对象,再以接口的形式调用GetHandler()方法来获取一个IHttpHandler实例。
回到顶部IHttpHandler.IsReusableIHttpHandler接口有个IsReusable属性。MSDN对这个属性的说明也非常简单:
当我连续5次访问 http://localhost:51652/abc.test?id=1 时,会在浏览器中看到以下输出结果:
从这个截图来看,显然:MyTestHandler的实例被重用了。
?
我想很多人都创建过ashx文件,IDE会为我们创建一个实现了IHttpHandler接口的类型,在实现IsReusable属性时,一般都会这样:
有了这个配置后,我们可以创建一个Handler2.ashx来测试效果: 『点击此处展开』
在多次访问Handler2.ashx后,我们可以看到以下效果:
再来看看按照IDE默认生成的IsReusable会在运行时出现什么结果。示例代码: 『点击此处展开』
此时,无论我访问Handler1.ashx多少次,浏览器始终显示如下结果:
如果我启用代码行 throw new Exception("这里不起作用。"); 将会看到以下结果:
终于,我们期待的黄页出现了。
此时,如果我在web.config中将ReusableAshxHandlerFactory的注册配置注释起来,发现Handler1.ashx还是可以访问的。回想一下前面我们看到的IHttpHandlerFactory接口,它还定义了一个ReleaseHandler方法,这个方法又是做什么的呢?对于这个方法,MSDN也有一句简单的说明:
接着找WebServiceHandlerFactory,最后发现是这样创建的HttpHandler : 『点击此处展开』
internal IHttpHandler CoreGetHandler(Type type, HttpContext context, HttpRequest request, HttpResponse response){ // ..... 已删除一些无关的代码 bool isAsync = protocol.MethodInfo.IsAsync; bool enableSession = protocol.MethodAttribute.EnableSession; if( isAsync ) { if( enableSession ) { return new AsyncSessionHandler(protocol); } return new AsyncSessionlessHandler(protocol); } if( enableSession ) { return new SyncSessionHandler(protocol); } return new SyncSessionlessHandler(protocol);}这才是Factory嘛!
老实说,看到这几句话,我是眼前一亮:用HttpHandlerFactory来动态处理【是否支持Session】实在是太合适了。
这里有必要补充一下:
internal class SyncSessionHandler : SyncSessionlessHandler, IRequiresSessionState{}小结:HttpHandlerFactory用途并非是为了专门处理HttpHandler的重用,它只是一个Factory, WebServiceHandlerFactory从另一个角度向我们展示了HttpHandlerFactory在扩展性方面所体现的重要作用。






