• 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的实质。
  • 相关阅读:
    算法提高---概率计算
    全排列
    算法提高 最小方差生成树
    【洛谷】P1040 加分二叉树
    SPAF模板
    Bellman-Ford算法(有向图)
    Floyd算法
    Dijkstra算法
    蓝桥杯算法提高 递推求值 【矩阵快速幂】
    【动态规划】数字分组I
  • 原文地址:https://www.cnblogs.com/jasononline/p/767198.html
Copyright © 2020-2023  润新知