彭斌

[ 2006,2007 ASP.NET ' MVP ]
随笔 - 99, 文章 - 5, 评论 - 1027, 引用 - 16
数据加载中……

Community Server专题四:HttpHandler

 

Community Server专题四:HttpHandler

HttpHandler实现了ISAPI Extention的功能,他处理请求(Request)的信息和发送响应(Response)HttpHandler功能的实现通过实现IHttpHandler接口来达到。

看图先:

ASP.NET 管道处理的末端是HTTP Hander,其实每个Asp.netPage都实现了IHttpHander,在VS.net中的对象察看器中你可以证实这一点

具体的类是这样定义的:public class Page : TemplateControl, IhttpHandler

接口IHttpHandler的定义如下:

interface IHttpHandler

{

void ProcessRequest(HttpContext ctx);

bool IsReuseable get; }

}

接口中ProcessRequest是添加自己的代码进行相应处理的地方。IsReuseable属性指明该HttpHandler的实现类是否需要缓存。

CS中有很多继承IHttpHandler接口的类,我取出有代表性而又容易理解的一个做分析:找到CommunityServerComponents项目Components目录下的Redirect.cs文件,内容如下:

//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.Web;

namespace CommunityServer.Components
{
    
/// <summary>
    
/// Summary description for Redirect.
    
/// </summary>

    public class Redirect : IHttpHandler
    
{
        
public Redirect()
        
{
            
//
            
// TODO: Add constructor logic here
            
//
        }


        
public void ProcessRequest(HttpContext context)
        
{
            
string url = context.Request.QueryString["u"];
            
if(!Globals.IsNullorEmpty(url))
            
{
                context.Response.Redirect(url);
                
            }

            
else
            
{
                context.Response.Redirect(Globals.GetSiteUrls().Home);
            }


            context.Response.End();
        }


        
public bool IsReusable
        
{
            
get return false; }
        }

    }

}

这里的Redirect功能是在web请求满足HttpHandler配置文件中预设条件下自动拦截执行的,在web.config<httpHandlers>节点下可以看到

<add verb="GET" path="Utility/redirect.aspx" type="CommunityServer.Components.Redirect, CommunityServer.Components" />


对该类的配置

· verb可以是"GET""POST",表示对GETPOST的请求进行处理。"*"表示对所有请求进行处理,这里是对GET请求进行处理。

· path指明对相应的文件进行处理,"*.aspx"表示对发给所有ASPX页面的请求进行处理,这里单独对redirect.aspx页面进行处理。可以指明路径,如"blogs"。表明只对blogs目录下的redirect.aspx文件请求进行处理。

· type属性中,逗号前的字符串指明HttpHandler的实现类的类名,后面的字符串指明Dll文件的名称。

实际处理是怎么样的呢?其实redirect.aspx页面在CS项目中并不存在,CS把对redirect.aspx的请求处理交给了CommunityServer.Components.dll程序集中Redirect类进行处理。处理的过程是执行

public void ProcessRequest(HttpContext context)

方法(Redirect类下的ProcessRequest方法是对当前请求的上下文ContextUrl是否包含“u”参数,如果有并且参数值不为null就调用Response.Redirect方法跳转到“u”参数值所执行的页面,如果没有参数或者参数值为空就跳转到CS的首页)

另外在CS中对RSSTrackback的处理都使用了httpHandler的处理方式。

前面提到,所有页面的基类Page都实现了HttpHandler接口,因此每个asp.net的页面都可以看成是一个HttpHandler处理类,只是配置部分在machine.config

 

<httpHandlers>
            
<add verb="*" path="*.vjsproj" type="System.Web.HttpForbiddenHandler"/><add verb="*" path="*.java" type="System.Web.HttpForbiddenHandler"/><add verb="*" path="*.jsl" type="System.Web.HttpForbiddenHandler"/><add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
            
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
            
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
            
<add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
            
<add verb="*" path="*.rem" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false"/>
            
<add verb="*" path="*.soap" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false"/>
            
<add verb="*" path="*.asax" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.ascx" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="GET,HEAD" path="*.dll.config" type="System.Web.StaticFileHandler"/>
            
<add verb="GET,HEAD" path="*.exe.config" type="System.Web.StaticFileHandler"/>
            
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.cs" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.csproj" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.vb" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.vbproj" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.webinfo" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.asp" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.licx" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.resx" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="*" path="*.resources" type="System.Web.HttpForbiddenHandler"/>
            
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
            
<add verb="*" path="*" type="System.Web.HttpMethodNotAllowedHandler"/>
        
</httpHandlers>
 

借助一个工具:Reflector,看看Page类下的HttpHandler处理方法ProcessRequest都做了什么

[EditorBrowsable(EditorBrowsableState.Never)]
public void ProcessRequest(HttpContext context)
{
      
this.SetIntrinsics(context);
      
this.ProcessRequest();
}

private void SetIntrinsics(HttpContext context)
{
      
this._context = context;
      
this._request = context.Request;
      
this._response = context.Response;
      
this._application = context.Application;
      
this._cache = context.Cache;
      
if ((this._clientTarget != null&& (this._clientTarget.Length > 0))
      
{
            
this._request.ClientTarget = this._clientTarget;
      }

      
base.HookUpAutomaticHandlers();
}

private void ProcessRequest()
{
      Thread thread1 
= Thread.CurrentThread;
      CultureInfo info1 
= thread1.CurrentCulture;
      CultureInfo info2 
= thread1.CurrentUICulture;
      
this.FrameworkInitialize();
      
try
      
{
            
try
            
{
                  
if (this.IsTransacted)
                  
{
                        
this.ProcessRequestTransacted();
                  }

                  
else
                  
{
                        
this.ProcessRequestMain();
                  }

                  
this.ProcessRequestEndTrace();
            }

            
finally
            
{
                  
this.ProcessRequestCleanup();
                  InternalSecurityPermissions.ControlThread.Assert();
                  thread1.CurrentCulture 
= info1;
                  thread1.CurrentUICulture 
= info2;
            }

      }

      
catch
      
{
            
throw;
      }

}


 

Page类的ProcessRequest方法先是把上下文Context内容赋值到当前Page中的一些属性里,然后调用System.Web.UI.TemplateControl中的HookUpAutomaticHandlers()

internal void HookUpAutomaticHandlers()
{
      
if (this.SupportAutoEvents)
      
{
            SimpleBitVector32 vector1 
= new SimpleBitVector32(this.AutoHandlers);
            InternalSecurityPermissions.Reflection.Assert();
            
if (!vector1[1])
            
{
                  vector1[
1= true;
                  
this.GetDelegateInformation("Page_Init"ref vector1, 24);
                  
this.GetDelegateInformation("Page_Load"ref vector1, 80x10);
                  
this.GetDelegateInformation("Page_DataBind"ref vector1, 0x200x40);
                  
this.GetDelegateInformation("Page_PreRender"ref vector1, 0x800x100);
                  
this.GetDelegateInformation("Page_Unload"ref vector1, 0x2000x400);
                  
this.GetDelegateInformation("Page_Error"ref vector1, 0x8000x1000);
                  
this.GetDelegateInformation("Page_AbortTransaction"ref vector1, 0x20000x4000);
                  
this.GetDelegateInformation("OnTransactionAbort"ref vector1, 0x80000x10000);
                  
this.GetDelegateInformation("Page_CommitTransaction"ref vector1, 0x200000x40000);
                  
this.GetDelegateInformation("OnTransactionCommit"ref vector1, 0x800000x100000);
                  
this.AutoHandlers = vector1.Data;
            }

            
if (vector1[2])
            
{
                  
base.Init += this.GetDelegateFromMethodName("Page_Init", vector1[4]);
            }

            
if (vector1[8])
            
{
                  
base.Load += this.GetDelegateFromMethodName("Page_Load", vector1[0x10]);
            }

            
if (vector1[0x20])
            
{
                  
base.DataBinding += this.GetDelegateFromMethodName("Page_DataBind", vector1[0x40]);
            }

            
if (vector1[0x80])
            
{
                  
base.PreRender += this.GetDelegateFromMethodName("Page_PreRender", vector1[0x100]);
            }

            
if (vector1[0x200])
            
{
                  
base.Unload += this.GetDelegateFromMethodName("Page_Unload", vector1[0x400]);
            }

            
if (vector1[0x800])
            
{
                  
this.Error += this.GetDelegateFromMethodName("Page_Error", vector1[0x1000]);
            }

            
if (vector1[0x2000])
            
{
                  
this.AbortTransaction += this.GetDelegateFromMethodName("Page_AbortTransaction", vector1[0x4000]);
            }

            
else if (vector1[0x8000])
            
{
                  
this.AbortTransaction += this.GetDelegateFromMethodName("OnTransactionAbort", vector1[0x10000]);
            }

            
if (vector1[0x20000])
            
{
                  
this.CommitTransaction += this.GetDelegateFromMethodName("Page_CommitTransaction", vector1[0x40000]);
            }

            
else if (vector1[0x80000])
            
{
                  
this.CommitTransaction += this.GetDelegateFromMethodName("OnTransactionCommit", vector1[0x100000]);
            }

      }

}



方法连接一些Handlers,通过委托加载相关的事件进行页面的初始化工作。

我不再往下分析,整个Page页面很庞大,有兴趣的朋友自己慢慢研究,说这些只是要明白一点,任何一个Page页面都是HttpHandler,页面处理是从这里开始。



.Text的早期版本中(很久没有看.Text的代码了)安装时要配置IIS

asp.net管道处理的级别上对一些扩展名称做映射,如*.html(其实.Text是做了*.*ASP.NET映射,够狠!,该扩展名的文件原本直接由IIS提交给请求者而不会经过asp.net处理机制处理,因此asp.net管道中的httpHandler是不可能拦截到的,但是只要做如下操作

这时,IIS会把对*.html文件的请求也交由ASP.NET机制去处理,继承IHttpHandler就可以拦截它(.Text中实际继承的是IHttpHandlerFactory)。有了这些知识就不难理解为什么你在.Text系统下察看blog时请求的是html文件(其实请求的html文件根本就不存在),返回的信息确是动态页面的内容,这与大型CMS系统不同,并不是为了节约系统资源预先生成htmlDotText之所以这样做牵涉到PermalinkSearch Engine Friendly,有这方面兴趣的朋友可以在google找到很多相关资源。


    虽然
CS中的blog.Text有密不可分的关系,但是CS中的blog已经没有采取早期.TextUrl扩展名为.html的访问机制,而是直接采用.aspx扩展名,也许更多考虑的是CS的部署问题,毕竟不是所有的CS使用者都会有可以自己配置IIS扩展名映射的权限,大多数虚拟主机也是不提供这样功能的。

HttpHandler还可以用来处理图片与下载的盗链问题,先在IIS中添加一些常用图片扩展名的映射到IIS中用Asp.net来处理对它的请求,这样就可以通过继承IHttpHandler的类和适当的配置来处理用户的请求,大致说一下过程:

在实现IHttpHandler接口的类中,ProcessRequest方法先用判断上次请求的URL(为什么是判断上次请求的URL,请看我上一篇专题中对IIS运行过程的讲解),如果这个URL是在你限定内那么就调用Response.WriteFile方法输出文件,如果不是,你可以自己采取一些措施,比如输出一个带有警告字样的图片等等。当然,使用HttpHandler处理图片与下载盗链问题是需要IIS控制权限的,而且需要浪费一些系统资源。

posted on 2005-09-07 11:34 彭斌 阅读(7688) 评论(29)  编辑 收藏 网摘 所属分类: CommunityServer

评论

#1楼   回复  引用  查看    

又来了,先支持一下。
2005-09-07 13:27 | CoolBug      

#2楼   回复  引用    

好好瞅瞅,顶一下。
2005-09-07 14:26 | 安文[未注册用户]

#3楼   回复  引用    

講解得很好﹐能不能講一下IIS權限控制這方面的知識?
2005-09-07 15:14 | FAR[未注册用户]

#4楼[楼主]   回复  引用  查看    

@FAR
请关注我下一个专题,对HttpModule&HttpHandler做总结,我会突出讲解IIS权限控制问题
2005-09-07 15:17 | uGoer      

#5楼   回复  引用    

学习中......
2005-09-07 15:42 | wngwz[未注册用户]

#6楼   回复  引用  查看    

应该讲一讲IHttpHandlerFactory
2005-09-07 17:07 | Vincent Trent      

#7楼   回复  引用  查看    

好文章!
2005-09-07 21:31 | dudu      

#8楼   回复  引用  查看    

还有一点我一直没想明白:相对httpmodule这种过滤器来说,httphandler是终端处理,不能一个web请求经过几个handler。那么相对直接写普通的aspx后台代码,它的优势何在呢?换句话说,什么时候应该像一般做法写个继承Page的类来处理,什么时候用httphandler来处理好呢?毕竟写handler没有写Page子类来得方便。
2005-09-07 23:04 | Vincent Trent      

#9楼[楼主]   回复  引用  查看    

@Vincent Trent
运用层次的不同,决定是否要使用handler。
1:一个小的项目,要的可能是开发速度,而且软件的使用周期也很有限,再或者根本就不会再去维护与扩展,这个时候你没有必要把handler引入进来,你需要的是再最短的时间内完成业务逻辑。
2:当你期望或者客户要求软件中一些功能在普通的aspx后台代码无法完成的时候,而用handler确可以完成,比如防盗链的运用。
3:自定义handler功能其实是在所有aspx页面执行之前完成的,这个时候请求的上下文Context中只有一些基本的信息,而Page继承了IhttpHandler的同时为了呈现页面还继承了TemplateControl, 也就是说初始化一个page页面比处理一个IhttpHandler要更多的系统资源开销,如在CS中对Utility/redirect.aspx页面的请求只是想实现redirect功能,根本不需要其他的页面呈现部分,因此采用了httpHandler,整个redirect都在服务器端实现,节约了一定的系统开销和数据回发的网络资源。
2005-09-08 14:39 | uGoer      

#10楼   回复  引用  查看    

有道理。不过关于第二点,防盗连似乎page也可以做到吧。Page里也可以获取Url引用的。对其它context, application, server等对象也可以访问到,不知有什么是它不能而httphandler却可以做到的?
2005-09-08 17:18 | Vincent Trent      

#11楼[楼主]   回复  引用  查看    

@Vincent Trent
例子:如http://www.****.com/upload/xxx.rar

在IIS没有做*.rar到asp.net映射之前,这个请求根本不经过asp.net机制的处理,映射后,就可以通过handler拦截到。但是page页面无论如何也处理不了这个问题。
可能你理解成为对文件的请求是对*.aspx页面的请求,然后由*.aspx验证后才把地址转向到所请求的文件地址,但是聪明的盗链者会通过N种手段绕过你的验证而直接发出对文件的请求,而你也只有千方百计地隐藏的分,没有什么其他主动的处理方法。

如果还是不能理解,请继续留言讨论。
2005-09-08 19:08 | uGoer      

#12楼   回复  引用  查看    

thanks. 总算明白了 ^_^
2005-09-09 08:54 | Vincent Trent      

#13楼   回复  引用  查看    

爽呀,太爽了,搞通了
2005-09-10 15:02 | 星仔      

#14楼   回复  引用  查看    

有一点不明:
实现IHttpHandle接口的Handle可以重写IsReusable属性
表示是否需要缓存的意思 那么这里缓存的东西是什么?缓存的时间长度是如何的 ?
2005-09-11 18:23 | kwklover      

#15楼[楼主]   回复  引用  查看    

@kwklover
这里缓存的是一个IHttpHandle实例。
如果 IHttpHandler 实例可再次使用,则为 true;否则,为 false。
该属性经常配合HttpHandlerFactory使用,判断一个Handle实例是否为多个请求提供服务。
2005-09-12 09:57 | uGoer      

#16楼   回复  引用  查看    

专题和讨论都非常好,文章好像有一点笔误,(不知道是不是我知识错误).
"type属性中,逗号前的字符串指明HttpHandler的实现类的类名,后面的字符串指明Dll文件的名称。"--->应该是程序集名称(名字),而不是文件名称吧?
2005-09-20 15:21 | §猪阿不猪§      

#17楼[楼主]   回复  引用  查看    

@§猪阿不猪§
你说的是对的,但是Dll文件的名称,如MemberRole.dll,这里的MemberRole就是文件名称了,.dll是后缀,你说的程序集名称,也就是MemberRole
2005-09-20 15:52 | uGoer      

#18楼   回复  引用  查看    

很受启发啊!
2005-10-15 15:23 | Alpha      

#19楼   回复  引用    

学习~
不知道可否在实现了IHttpHandler的类中抓取异常信息, 以此来自定义处理任何异常
2005-10-30 01:15 | qfcn[未注册用户]

#20楼[楼主]   回复  引用  查看    

@qfcn
IHttpModule中可以捕获全局的错误
application.Error += new EventHandler(this.Application_OnError);

可以看我这篇文章:http://ugoer.cnblogs.com/archive/2005/09/06/230917.html">http://ugoer.cnblogs.com/archive/2005/09/06/230917.html
2005-10-31 09:09 | uGoer      

#21楼   回复  引用    

写的很好!
但是好像不能实现可删节的功能
http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/10">http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/10/或">http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/10">http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/10/或
http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/10">http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/10
--==>http://xxxxSite/proshow.aspx?y=2005&m=01&d=10">http://xxxxSite/proshow.aspx?y=2005&m=01&d=10
http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/或">http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01/或
http://xxxxSite/product/2005/01">http://xxxxSite/product/2005/01
--==>http://xxxxSite/proshow.aspx?y=2005&m=01
http://xxxxSite/product/2005/或">http://xxxxSite/product/2005/或
http://xxxxSite/product/2005

--==>http://xxxxSite/proshow.aspx?y=2005&m=01&d=10">http://xxxxSite/proshow.aspx?y=2005&m=01&d=10
2005-11-04 14:48 | dhz[未注册用户]

#22楼   回复  引用  查看    

@ dhz:
写的很好!
但是好像不能实现可删节的功能
要达到你的目的,必须使用 *.*到Asp.net的映射
2005-12-06 21:09 | THIN      

#23楼   回复  引用    

真牛。有点个人崇拜了!!!
2005-12-15 17:23 | TalkStudy[未注册用户]

#24楼   回复  引用    

请教一个问题,为什么cs中使用了大量得context来存储数据?
如果context用来存储会话中得数据,且在内存上,为什么不用Session呢?
Session也很好用,此外,它跟Cookie,Apllication等有什么区别呢?
可能我问得比较多,给个文章链接也行!:)
2007-01-30 10:43 | bit[未注册用户]

#25楼   回复  引用    

请教一个问题,为什么cs中使用了大量得context来存储数据?
如果context用来存储会话中得数据,且在内存上,为什么不用Session呢?
Session也很好用,此外,它跟Cookie,Apllication等有什么区别呢?
----------------
这个我也想知道。


我对 *.aspx 做了一个HttpHandler,里面就是放一下判断,然后要把当前的*.aspx页面转到那个页面本身,该怎么弄呢?
context.Response.Redirect(url) 好像会死循环,浏览器下面的进度条一直在跳,
我用HttpContext.Current.Server.Execute 它就报错
2007-02-06 18:01 | baal[未注册用户]

#26楼   回复  引用    

这个CS中有
看类Redirect就可以了
<add verb="GET" path="Utility/redirect.aspx" type="CommunityServer.Components.Redirect, CommunityServer.Components" />
2007-02-08 10:49 | bit[未注册用户]

#27楼   回复  引用    

http://*** 模型
2007-07-25 15:10 | 模型模型[未注册用户]

#28楼   回复  引用  查看    

哈哈。。。很棒。
2008-03-22 22:52 | 破曉之陽      

#29楼   回复  引用  查看    

我看了一下午,也没有看懂,看你们一个个评论的那么兴奋
2009-03-17 14:58 | 一过      



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 231676




相关文章:

相关链接: