详谈关于Discuz!NT 的URL地址重写(URLRewrite)

在Discuz!NT中的前台页面访问(特别是aspx)是被HttpModule接管的,所以大家在Discuz.Web项目的目录下看到的唯一”aspx文件”是index.aspx,而所有其它前台页面都有“/aspx/”文件夹下的相应的子目录中,而这些子目录名称是与后台所“生成”的模板存在对应关系的。而这种“关系”的绑定是通过dnt_templates(模板数据表)来进行关联的。而有关模板机制的文章详见:

“Discuz!NT 模板机制分析”一文。 今天所要说的其实是模板机制的“延续”,当然这种“延续”仅是我个人的观点。因为地址重写最终要绑定的路径,恰恰与模板机制是有着前后呼应的关系。首先请大家看一下这张图:

图1 :[discuz!NT 的web.config文件相关配置节] 相信对于那些以前做过“URL重写”的朋友看到这一行配置的第一反映可能就是要去Discuz.Forum.HttpModule类中一看究竟。同时考虑到在<httpModules>配置节中加入处理的资料在网上有许多,所以这里就不再多费唇舌了。

另外如果大家感兴趣味的话,也可以抽空看看我的这篇文章,”NET框架中的 Observer 模式”。虽然该文是我在挖.net底层代码时是写的一些个人想法,可读性不高,但我相信会对大家有所帮助的:)

好了,下面让我们看一下在Discuz.Forum项目中的HttpModule类中的一些信息,看看URL重写到底如何实现:)

1///<summary>2///论坛HttpModule类3///</summary>4publicclassHttpModule:System.Web.IHttpModule5{6///<summary>7///实现接口的Init方法8///</summary>9///<paramname=”context”></param>10publicvoidInit(HttpApplicationcontext)11{12OnlineUsers.ResetOnlineList();13context.BeginRequest+=newEventHandler(ReUrl_BeginRequest);14}15161718

上面代码中的Init(HttpApplication context)是HttpModule类进行操作的入口,所有实现System.Web.IHttpModule 接口的类都必须实现这个函数。 同时大家看到的OnlineUsers.ResetOnlineList()方法主要是用于“重置(复位)在线表”,而有关“在线用户”的功能我会在以后专门写文章来加以介绍,所以这里大家只要知道它要干的事(代码如下)即可:)

1///<summary>2///复位在线表,如果系统未重启,仅是应用程序重新启动,则不会重新创建3///</summary>4///<returns></returns>5publicstaticintResetOnlineList()6{7try8{9//取得在线表最后一条记录的tickcount字段(因为本功能不要求特别精确)10//inttickcount=DatabaseProvider.GetInstance().GetLastTickCount();11//如果距离现在系统运行时间小于10分钟12if(System.Environment.TickCount<600000)13{14returnInitOnlineList();15}16return-1;17}18catch19{20try21{22returnInitOnlineList();23}24catch25{26return-1;27}28}2930}3132

而紧随其后的事件绑定代码就是一个关键点了,形如:

context.BeginRequest+=newEventHandler(ReUrl_BeginRequest);

因为当通过浏览器提交请求时,IIS都会接管请求进行相应的操作(详见DUDU的文章:”ASP.NET 2.0运行时简要分析”)后,最终通过Web.config中的相应配置节(上图所示)来执行用户预定的处理操作。而我们的代码就在ReUrl_BeginRequest事件中(代码如下,详情见注释):

1///<summary>2///重写Url3///</summary>4///<paramname=”sender”>事件的源</param>5///<paramname=”e”>包含事件数据的EventArgs</param>6privatevoidReUrl_BeginRequest(objectsender,EventArgse)7{8//获取基本配置信息9BaseConfigInfobaseconfig=BaseConfigProvider.Instance();1011if(baseconfig==null)12{13return;14}1516//得到论坛配置信息17GeneralConfigInfoconfig=GeneralConfigs.GetConfig();18HttpContextcontext=((HttpApplication)sender).Context;19stringforumPath=baseconfig.Forumpath.ToLower();2021stringrequestPath=context.Request.Path.ToLower();2223if(requestPath.StartsWith(forumPath))24{25if(requestPath.Substring(forumPath.Length).IndexOf(“/”)==-1)26{27//声明并设置默认论坛模板28stringstrTemplateid=config.Templateid.ToString();29//判断COOKIE中模板是否是系统当前的有效(已入库)模板30if(Utils.InArray(Utils.GetCookie(Utils.GetTemplateCookieName()),31Templates.GetValidTemplateIDList()))32{33strTemplateid=Utils.GetCookie(Utils.GetTemplateCookieName());34}3536//当访问的是首页时37if(requestPath.EndsWith(“/index.aspx”))38{39//当配置文件中未指定首页时,则将forumindex.aspx做为首页并重写路径40if(config.Indexpage==0)41{4243context.RewritePath(forumPath+”aspx/”+strTemplateid+”/forumindex.aspx”);44}45else//否则使用聚合首页来做为网站首页并重写路径46{4748context.RewritePath(forumPath+”aspx/”+strTemplateid+”/website.aspx”);49}5051return;52}535455//当使用伪aspx,如:showforum-1.aspx等.56if(config.Aspxrewrite==1)57{58//获取后台设置的可以使用的伪aspx设置.59//SiteUrls.GetSiteUrls()类和方法说明见下文60foreach(SiteUrls.URLRewriteurlinSiteUrls.GetSiteUrls().Urls)61{62//进行正则匹配,来看访问页面是否是用户定义的伪URL地址63if(Regex.IsMatch(requestPath,url.Pattern,Utils.GetRegexCompiledOptions()|RegexOptions.IgnoreCase))64{65stringnewUrl=Regex.Replace(requestPath.Substring(context.Request.Path.LastIndexOf(“/”)),url.Pattern,url.QueryString,Utils.GetRegexCompiledOptions()|RegexOptions.IgnoreCase);6667context.RewritePath(forumPath+”aspx/”+strTemplateid+url.Page,string.Empty,newUrl);68return;69}70}71}72737475context.RewritePath(forumPath+”aspx/”+strTemplateid+requestPath.Substring(context.Request.Path.LastIndexOf(“/”)),string.Empty,context.Request.QueryString.ToString());76}77//当前请求路径是“论坛路径/archiver(简洁版路径)/”下时.78elseif(requestPath.StartsWith(forumPath+”archiver/”))79{808182return;83}84//当前请求路径是“论坛路径/tools/(论坛工具页面如:rss,sitemap,help等)”请求时85elseif(requestPath.StartsWith(forumPath+”tools/”))86{8788return;89}9091}92}9394

这样就实现了伪URL的动态转换(地址重写)。而相应的“SiteUrls”类则是对伪URL设置信息进行访问读取的“封装类”,目的就是要将Discuz.Web项目中config文件夹下的urls.config文件转换成可访问的信息对象。形如:

1///<summary>2///站点伪Url信息类3///</summary>4publicclassSiteUrls5{6内部属性和方法6667//获取伪URL设置对象的实例68publicstaticSiteUrlsGetSiteUrls()69{70if(instance==null)71{72lock(lockHelper)73{74if(instance==null)75{76instance=newSiteUrls();77}78}79}80returninstance;8182}8384publicstaticvoidSetInstance(SiteUrlsanInstance)85{86if(anInstance!=null)87instance=anInstance;88}8990publicstaticvoidSetInstance()91{92SetInstance(newSiteUrls());93}949596///<summary>97///重写伪地址(内部类),用于声明和封装对象98///</summary>99publicclassURLRewrite100{101成员变量154155构造函数164}165166}167168

因为代码很简单,这里就不再另行说明了。

到目前,我们知道了要通过“urls.config”文件来进行伪URL重写,那么这个文件中的数据到底是什么样子呢,下面就是在成功安装discuz!nt 2.0产品之后的文件内容:

<?xmlversion=”1.0″encoding=”utf-8″?><urls><rewritename=”showforum”path=”/showforum-{0}-{1}.aspx”pattern=”/showforum-(/d+)(-(/d+))?.aspx”page=”/showforum.aspx”querystring=”forumid=$1^page=$3″/><rewritename=”showtopic”path=”/showtopic-{0}-{1}.aspx”pattern=”/showtopic-(/d+)(-(/d+))?.aspx”page=”/showtopic.aspx”querystring=”topicid=$1^page=$3″/><rewritename=”userinfo”path=”/userinfo-{0}.aspx”pattern=”/userinfo-(/d+)*.aspx”page=”/userinfo.aspx”querystring=”userid=$1″/><rewritename=”rss”path=”/rss-{0}.aspx”pattern=”/rss-(/d+)?.aspx”page=”/rss.aspx”querystring=”forumid=$1″/><rewritename=”spacerss”path=”/spacerss-{0}.aspx”pattern=”/spacerss(-(/d+))?.aspx(/?.*?)?”page=”/rss.aspx”querystring=”uid=$2&amp;type=space”/><rewritename=”photorss”path=”/photorss-{0}.aspx”pattern=”/photorss(-(/d+))?.aspx(/?.*?)?”page=”/rss.aspx”querystring=”uid=$2&amp;type=photo”/></urls>

大家看到了吧。就目前而言伪URL重写的页面(page节点)包括如下几个:

showforum.aspx(版块列表),showtopic.aspx(主题列表),userinfo.aspx(用户信息),rss.aspx,虽然不多,但已基本满足了设置需要。

而设置(配置)管理的页面在后台的“全局”–>“常规选项”–>“基本设置”–>“编辑伪静态url替换规则”,如果所示: 相信聊到这里大家对我们产品的这项功能应该有个大概认识了,但这不并是全部的设置,因为如果要接管形如”showforum-1-2.htm”这样的页面,是必须要到IIS里去“搞一把”的。因为默认的IIS中对htm(html)的处理是无法实现将上面的链接转成“showforum.aspx?forumid=1&page=2”的形式的,所以这里要参考一下官方文档(使用伪Url地址)中的方法,相应图示如下:

好的,下面再接着聊一下关于与第三方产品“整和”的问题。因为目前大多数采用 .net技术开发的主流产品(如博客园等)都会或多或少使用与本文类似或相同的做法来实现地址重写功能。比如:继承并实现IHttpHandler接口。 当然我们的产品所采用实现IHttpModule的方式会比IHttpHandler更早一步被 IIS进行处理。所以如果出现了这种情况,我个人建议是改动我们产品中的代码,通过在上述 ReUrl_BeginRequest事件中加入条件分支(第三方程序的伪地址规则)来实现“地址重写整合”;当然如果第三方的 (重写)规则太复杂的话,也可以在我们产品的相应代码中加入条件分支(只要出现第三方请求的链接页面或指定路径时),但不作任何处理(直接绿灯放行),这样就会转入到第三方的伪URL重写规则代码的“势力范围”内了。

当然如果第三方也使用类似IHttpModule的实现方式,则要看是谁的配置先出现在web.config中的相应配置节中了。因为“先入为主”嘛,如果先后顺序已明确的话,那么先被加载的HttpModule 模块就要做一下变动了,以便不会干扰后面的HttpModule模块的正常运行了。因为我手上这方面的实例也不多,所以只能在这里聊聊我本人的一些看法,相信随着官方“整合案例” 的增加,这方面的资料会得到更大的补充。

就会犯错误,就会有无数次让自己跌倒的机会出现,

详谈关于Discuz!NT 的URL地址重写(URLRewrite)

相关文章:

你感兴趣的文章:

标签云: