现在的位置: 首页 > 综合 > 正文

分享CommunityServer(5) –UrlRewrite

2013年08月17日 ⁄ 综合 ⁄ 共 6833字 ⁄ 字号 评论关闭
 

大凡Context,根据单词的字面意义,我们大概都知道是关于某些进程或者线程的执行上下文,通常asp.net的请求会为每一个请求准备一个执行管道,也会准备一个执行环境上下文,这就是HttpContext。
CS认为HttpContext的信息并不直接,无法满足各项功能的直接需求,为了能够更好的处理当前请求,并与高度灵活的配置其他可定制部分协同处理执行请求,为此集成HttpContext实现了一个新的描述执行环境上下文CSContext.
由于需要通常Context在asp.net执行环境来说通常(不通常才怪)是单例的(这需要分析asp.net的执行原理http请求的生命周期,并非本人在此探讨的重点),所以往往这类对象的构造函数也是单例实现的。查看CSContext的构造函数,都只有私有的构造函数,并且public的只有静态实例化函数:
     public static CSContext CreateEmptyContext()     //创建空的执行上下文对象
         {
              CSContext csContext = new CSContext(new Uri("http://CreateEmptyContext"),"http://CreateEmptyContext");
              csContext._isEmpty = true;
              SaveContextToStore(csContext);
              return csContext;
         }
 
     public static CSContext Create(int settingsID)   //根据站点ID来构造执行上下文对象
         {
            SiteSettings siteSettings = SiteSettingsManager.GetSiteSettings(settingsID);
            CSContext csContext;
            try
            {
                csContext = new CSContext(new Uri(siteSettings.SiteDomainUrl), siteSettings.SiteDomainUrl);
            }
              catch(Exception ex)
             {
                CSException csException = new CSException(CSExceptionType.UnknownError, string.Format("CSContext failed to load a valid url for settingsid {0}. Url in database {1}. Reason {2}", settingsID, siteSettings.SiteDomainUrl, ex.Message));
                csException.Log(settingsID);
               
                 csContext = new CSContext(new Uri("http://CreateEmptyContext"), "http://CreateEmptyContext");
            }
              csContext.SiteSettings = siteSettings;
              SaveContextToStore(csContext);
              return csContext;
         }
 
public static CSContext Create(HttpContext context)   //利用HttpContext来封装执行上下文
         {
              return Create(context,false);
         }
 
public static CSContext Create(HttpContext context, bool isReWritten) //是否允许url重写?
         {
              CSContext csContext = new CSContext(context,true);
              csContext.IsUrlReWritten = isReWritten;
              SaveContextToStore(csContext);
              return csContext;
         }
//携带一个Url重写指代来构造执行上下文对象
public static CSContext Create(HttpContext context, UrlReWriterDelegate rewriter)
        {
            CSContext csContext = new CSContext(context,false);
            SaveContextToStore(csContext);
            csContext.IsUrlReWritten = rewriter(context);
            csContext._queryString = new NameValueCollection(context.Request.QueryString);
            return csContext;
        }
 
public static CSContext Create(Uri uri, string appName)//根据Uri和应用程序名来构造
         {
              CSContext csContext = new CSContext(uri,appName);
              SaveContextToStore(csContext);
              return csContext;
         }
那么在那些关键的地方执行了CSContext的实例化呢?
分析用户验证的时候,了解到CSHttpModule有对于整个CS应用程序的事件处理挂接,其中 application.BeginRequest += new EventHandler(this.Application_BeginRequest);对应的挂接函数是这样的:
private void Application_BeginRequest(Object source, EventArgs e)
         {
              HttpApplication application = (HttpApplication)source;
          HttpContext context = application.Context;
              if (context.Request.RawUrl.IndexOfAny(new char[] {'<', '>', '/'', '"'}) != -1)
              {//在urlrewrite之前进行url的编码转换工作
                   context.Response.Redirect(context.Request.RawUrl.Replace("<", "%3c").Replace(">", "%3e").Replace("/'", "%27").Replace("/"", "%22"));
                   return;
               
              }
              CSConfiguration config = CSConfiguration.GetConfig();//获得当前配置        
              // 如果是installer那么就终止当前执行
              if (config.AppLocation.CurrentApplicationType == ApplicationType.Installer)
              {
                   return;
              }
              CheckWWWStatus(config,context);
         CheckSSL(config.SSL,context);     
              CSContext.Create(context, new UrlReWriterDelegate(ReWriteUrl));//此处进行了含有Url重写的指代的CsContext构造
         }
CSContext.Create(context, new UrlReWriterDelegate(ReWriteUrl));会执行特定的指代函数
     private bool ReWriteUrl(HttpContext context)
         {
            UrlReWriteProvider urlProvider = UrlReWriteProvider.Instance();//provider模式,幸好我们之前已经分析了,不过
              string path = context.Request.Path;
              string newPath = UrlReWriteProvider.Instance().GetRewrittenUrl(path,context.Request.Url.Query);                          if(newPath != null)
              {
                   string qs = null;
                   int index = newPath.IndexOf('?');
                   if (index >= 0)
                   {
                       qs = (index < (newPath.Length - 1)) ? newPath.Substring(index + 1) : string.Empty;
                        newPath = newPath.Substring(0, index);
                   }
 
                urlProvider.RewriteUrl(context, newPath, qs);
              }
 
              return newPath != null;
         }
UrlReWriteProvider是一个抽象类,类似前面分析的Provider模式,但是具体的在我收到的CS版本中并没有CommunityServer.config中提供具体的provder组件,而是如下:
static UrlReWriteProvider()
        {
            Type defaultProviderType = null;
 
              if (Environment.Version.Major >= 2)
            {
                defaultProviderType = Type.GetType("CommunityServer.Components.CSASPNET20UrlReWriteProvider, CommunityServer.Components");
            }
 
            if(defaultProviderType == null)
                defaultProviderType = typeof(CSUrlReWriter);
 
            _instance = SingletonProviderHelper.LoadInstance("UrlReWriteProvider", defaultProviderType) as UrlReWriteProvider;
        }
如果是asp.net2.0以上版本,则使用CSASPNET20UrlReWriteProvider.cs中指定的UrlReriter来实现;如果是1.X的版本那么就读取provider配置节中UrlReWriteProvider指定的组件;但是由于communityServer.config并不存在指定的 UrlReWriteProvider 所以就直接使用 defaultProviderType = typeof(CSUrlReWriter);来替代。通常,我们如果期望某个组件可以被替换,但是实际运行又必不可少,我们可以给他弄一个缺省的组件,实现基本的功能,如果运行时实际部署没有指定个性化版本的组件,那么我们就是用内置的(如此处的CSUrlReWriter)来实现基本的运行细节。
       static CSUrlReWriter()
        {          
            ReWriteFilter = new Regex(SiteUrls.Instance().LocationFilter, RegexOptions.IgnoreCase|RegexOptions.Compiled);
        }
CSUrlReWriter通过SiteUrls的实例获得了LocationFilter,而LocationFilter的数据来源自我们在web根目录下指定的 SiteUrls.config
对于全部url,会尝试正则式匹配,如果匹配,那么获得 所谓 Location对象,然后利用此对象的ReWriteUrl 方法获得新的Url
public virtual string ReWriteUrl(string path, string queryString)
        {
            string newPath = null;
            if(this.Count > 0)
            {
                foreach(ReWrittenUrl url in this)
                {
                    if(url.IsMatch(path))
                    {
                        newPath = url.Convert(path,queryString);
                        CSContext.Current.RewrittenUrlName = url.Name;
                        break;
                    }
                   
                }
            }
            return newPath;
        }
这样一个URL会经过UrlRewrite的指代获得转换(如果匹配的话)得到一个新的经过重写的URL。注意,asp.net1.1和asp.net2.0由于执行模式的改变,所以二者在重写的实现上是不同的(至于细节又需要一大篇幅了,在此就不花开两朵了)。
通过在HttpModual中利用HttpContext构造CSContext并利用缺省的UrlReWriteProvider(CSUrlReWriter)读取SiteUrls.config中的数据,获得Url的重写规则,并替换得到新的Url,最后还是使用asp.net框架提供的API context.RewritePath(path, null, queryString);来执行最终的Url。
总结下,通过分析url重写的逻辑我们大致可了解到:
1、 可以替换定制,利用provider模式实现的
2、 可以直接手动修改 SiteUrls.config中的数据,实现映射URL转换。
3、 正则式是高效的重写逻辑工具
4、 区分asp.net1.1和2.0的url重写的功能的不同实现方式
5、CSHttpModule基本上是Http请求的可控执行起点,无论是验证的身份确立还是Url重写都在此挂接,感谢asp.net框架的设计,允许我们安全有序的操控Http请求执行上下文。
 

 

抱歉!评论已关闭.