大凡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请求执行上下文。