dotText源码阅读(5)--URLreWrite和Handler

 Dottext需要映射全部不存在的文件到blog应用程序,实际上是需要IIS对于该应用下的问不进行处理,而是交给dottext程序处理,而dottext则利用一系列的handler来进行配置,对应不同的文件类型,或者匹配特定的文件,实现整个blog的URL 重写的。
        首先,是通过
<httpHandlers>
<addverb="*"path="*.asmx"type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"validate="false"/>
              <addverb="*"path="Error.aspx"type="System.Web.UI.PageHandlerFactory"/>
<addverb="*"path="*"type="Dottext.Common.UrlManager.UrlReWriteHandlerFactory,Dottext.Common"/>
          </httpHandlers>
确保了任何对blog所在应用程序的访问都会被以上3个handler处理,如果是扩展名.asmx的http请求,会被系统缺省的处理程序处理;而对于错误处理(大多数都是转到error.aspx)会转入到系统的缺省aspx处理程序,其他任何请求都会转到Dottext.Common.UrlManager.UrlReWriteHandlerFactory,Dottext.Common  所以我们首先来看看这个处理句柄:
这是一个工厂类型的执行句柄,他自身并不进行处理。而是负责将请求根据不同的类别进行分别派遣,调出不同的处理程序进行执行,而这构成了dottext高效处理整个blog运行的精妙设计部分。
protected virtual HttpHandler[] GetHttpHandlers(HttpContext context)
         {
              return HandlerConfiguration.Instance().HttpHandlers;//这是个收集
         }
IhttpHandler接口的实现,用于返回处理http请求的全部句柄。而句柄配置在web.config中,所以dottext是这样获得全部handler的。而HandlerConfiguration.Instance()类似我们前面分析的配置处理体系那里的处理过程:
public static HandlerConfiguration Instance()
         {
              return ((HandlerConfiguration)ConfigurationSettings.GetConfig("HandlerConfiguration"));
         }
是从配置文件的xml片断中,获得产生具体的类实例,并经过反序列化后(请看看HandlerConfiguration的属性定义),得到一个句柄的数组,返回给UrlReWriteHandlerFactory的调用函数。具体为:
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string path)
         {
              HttpHandler[] items = GetHttpHandlers(context);
              if(items != null)
              {
                   int count = items.Length;
                   string appStr=Dottext.Framework.Util.Globals.RemoveAppFromPath(context.Request.Path,context.Request.ApplicationPath);//得到访问哪一个应用程序的哪一个具体文件
                   for(int i = 0; i<count; i++)
                   {
                       //定向到特定的aspx文件
                        if(items[i].IsMatch(appStr))     //看看是否匹配系统配置中的者则表达式
                       {
                            //throw new Exception();
                            switch(items[i].HandlerType)
                            {
                                 case HandlerType.Page://默认是Page
                                     return ProccessHandlerTypePage(items[i],context,requestType,url);
                                 case HandlerType.Direct:                                   HandlerConfiguration.SetControls(context,items[i].BlogControls);
                                     return (IHttpHandler)items[i].Instance();
                                 case HandlerType.Factory:                                                                       return ((IHttpHandlerFactory)items[i].Instance()).GetHandler(context,requestType,url,path);
                                 default:
                                     throw new Exception("Invalid HandlerType: Unknown");
                            }
                       }
                   }
              }
              //如果请求的页面不匹配任何一个句柄,就使用ASP.NET的
              return PageHandlerFactory.GetHandler(context,requestType,url, path);
         }
获得全部web.config指定的handler以及正则表达式后,就进行匹配当前http访问请求的处理分析,如果请求的URL字符串匹配一个Page类型正则表达式(HttpHandler是一个实体类,通过反序列化后获得了type和Pattern属性,如果请求的资源是aspx或者html的,那么会进行正则式判断是否符合句柄的模式,)如果符合,那么同时就知道了句柄的类型,根据HandlerType.Page 、HandlerType.Direct、HandlerType.Factory进行分别处理。
如果是Page类型(某个aspx页面),那么执行:
private IHttpHandler ProccessHandlerTypePage(HttpHandler item, HttpContext context, string requestType, string url)
         {
              string pagepath = item.FullPageLocation;
              if(pagepath == null)
              {
                   pagepath = HandlerConfiguration.Instance().FullPageLocation;
              }
              HandlerConfiguration.SetControls(context,item.BlogControls);
              IHttpHandler myhandler=PageParser.GetCompiledPageInstance(url,pagepath,context);
              return myhandler;
         }
HandlerConfiguration.Instance()的代码如下:
public static HandlerConfiguration Instance()
         {
              return ((HandlerConfiguration)ConfigurationSettings.GetConfig("HandlerConfiguration"));
         }
同样,这也是配置文件通过反序列化得到一个HandlerConfiguration的实例,HandlerConfiguration的配置节内容在web.config中存在,我们会得到一个defaultPageLocation属性,FullPageLocation如果在属性无法获取的时候就返回defaultPageLocation的值,也就是说,通常我们访问某个目录,不带指定的aspx 的page文件名,就会自动访问defaultPageLocation.的指示的值。
SetControls 是针对部分页面的,就是类似<HttpHandlerpattern="/archive/\d{4}/\d{1,2}\.aspx$"controls="ArchiveMonth.ascx"/>这类page 的,通常是指向一个用户控件,而大家知道用户控件实际上就是一个page。SetControls会把控件加入到当前请求的context重,以便执行期间从context中区的控件。
PageParser对象实际上是asp.net的解释对对象,它将指定的资源编译成程序集,这类似一个普通的物理存在的aspx页面执行机制。大家注意到,返回的是一个IhttpHandler对象,实际上asp.net的任何一个page都应该实现这个接口的,所以此处的逻辑就相当于执行了一个存在的页面。虽然页面可能不存在,但是通过配置指定最后得到了一个IhttpHandler对象处理了用户的http请求,这是Page类型的处理过程简要描述。
     第二类HandlerType是Direct ,有以下的http资源请求定向到这类Handler:
<HttpHandlerpattern="(\.config|\.asax|\.ascx|\.config|\.cs|\.vb|\.vbproj|\.asp|\.licx|\.resx|\.resources)$"     type="Dottext.Framework.UrlManager.HttpForbiddenHandler, Dottext.Framework"handlerType="Direct"/>
              <HttpHandlerpattern="(\.gif|\.js|\.jpg|\.zip|\.jpeg|\.jpe|\.css|\.rar|\.xml|\.xsl)$"type="Dottext.Common.UrlManager.BlogStaticFileHandler, Dottext.Common"handlerType="Direct"/>    
 ......              <HttpHandlerpattern="/services\/pingback\.aspx$"type="Dottext.Framework.Tracking.PingBackService, Dottext.Framework"     handlerType="Direct"/>              <HttpHandlerpattern="/services\/metablogapi\.aspx$"type="Dottext.Framework.XmlRpc.MetaWeblog, Dottext.Framework"     handlerType="Direct"/>
              可以看到,大部分我们找不到实际的文件名,但是却可以通过访问blog下的url返回内容,系统根据url判断如何返回内容。我们举一个例子来看看Direct怎么执行的。看看<HttpHandlerpattern="/rss\.aspx$"type="Dottext.Common.Syndication.RssHandler, Dottext.Common"     handlerType="Direct"/>
执行:
case HandlerType.Direct:                                   HandlerConfiguration.SetControls(context,items[i].BlogControls);
                                     return (IHttpHandler)items[i].Instance();
时候,会实例化一个Dottext.Common.Syndication.RssHandler类的实例(RssHandler是间接实现了IhttpHandler接口的),它继承自抽象类BaseSyndicationHandler,BaseSyndicationHandler实现了总体的返回特定格式RSS文档的功能和能力,通过继承覆盖,不同的格式的实现类(RSS20和ATOM等)实现了各自格式的rss文档返回给用户。总之,在这类的handler中,最终通过Context.Response操纵到客户的输出流。
     第三类的是Factory类型的,其自身就是一个工厂模式的handler,会再次将当前url转交给下一级handler,这样实现了可扩展性。如果dottext的新功能需要进一步处理URL得到其他功能就可以利用此类进行处理。譬如:
<HttpHandlerpattern="/(?:admin)"type="Dottext.Web.UI.Handlers.BlogExistingPageHandler, Dottext.Web"handlerType="Factory"/>
当用户访问应用程序下的/admin目录时候,自然处于该Dottext.Web.UI.Handlers.BlogExistingPageHandle 处理。由于是工厂模式,所以我们着重看看:
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string path)
         {     BlogConfig config = Config.CurrentBlog(context);
              if(ConfigProvider.Instance().IsAggregateSite)
              {
                   string app = config.Application.ToLower();
                   url = Regex.Replace(url,app,"/",RegexOptions.IgnoreCase);
app = "\\\\"+config.CleanApplication+"\\\\";
                   path = Regex.Replace(path,app,"/",RegexOptions.IgnoreCase);
                   if(!Regex.IsMatch(path,"\\.\\w+$"))
                   {
                       path = System.IO.Path.Combine(path,"index.aspx");
                   }
              }
              return PageParser.GetCompiledPageInstance(url, path, context);
         }
处理时候,首先取得当前blog的配置,ConfigProvider.Instance()返回一个Iconfig接口的实例,看看这个Instance的代码:
     static ConfigProvider()         //静态构造函数
         {
              ConfigProviderConfiguration cpc = Config.Settings.BlogProviders.ConfigProvider;
              config = (IConfig)cpc.Instance();
              config.Application = cpc.Application;
              config.CacheTime = cpc.CacheTime;
              config.Host = cpc.Host;
              config.ImageDirectory = cpc.ImageDirectory;
              config.BlogID = cpc.BlogID;             
         }
          private static IConfig config = null;
         public static IConfig Instance()
         {
              return config;
         }
执行静态构造函数,通过Config.Settings.BlogProviders.ConfigProvider反序列化得到ConfigProviderConfiguration。ConfigProviderConfiguration继承抽象类BaseProvider,通过BaseProvider的instance方法:
public object Instance()
         {
              return Activator.CreateInstance(System.Type.GetType(this.ProviderType));
         }
此处的ProviderType是通过反序列化得到:
[XmlAttribute("type")]
         public string ProviderType
         {
              get {     return _type;   }
              set { _type = value; }
         }
也就是
<ConfigProvidertype="Dottext.Common.Config.MultipleBlogConfig, Dottext.Common"host="localhost"     cacheTime="120"/> 中指明的MultipleBlogConfig 类型。MultipleBlogConfig继承自BaseBlogConfig , BaseBlogConfig实现了IConfig,所以你才看到
config = (IConfig)cpc.Instance();
然后,得到了相应的属性IsAggregateSite。该属性的意思是当前访问的是否是聚合站点(而不是但个博客的站点,只有聚合站点才可以使用存在的aspx文件)。确认聚合站点后,就取得应用程序目录下的实际aspx文件,然后利用CLR的功能PageParser.GetCompiledPageInstance(url, path, context)返回页面执行结果。
所有blog的http请求,依据URL通过正则表达式匹配到不同的Handler类型,实现了3种类别的处理,但最终用户看到的是请求执行结果。修改web.config我们可以进行特定资源的特殊执行,这是UrlReWrite的实质。

posted on 2007-06-01 09:04  jasonCao  阅读(388)  评论(1编辑  收藏  举报

导航