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的實質。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章