詳談關於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重寫到
底如何實現:)
/// <summary>2
/// 論壇HttpModule類3
/// </summary>4
public class HttpModule : System.Web.IHttpModule5
{6
/// <summary>7
/// 實現接口的Init方法8
/// </summary>9
/// <param name="context"></param>10
public void Init(HttpApplication context)11
{12
OnlineUsers.ResetOnlineList();13
context.BeginRequest += new EventHandler(ReUrl_BeginRequest);14
}15

16
17

18
上麵代碼中的Init(HttpApplication context)是HttpModule類進行操作的入口,所有實現
System.Web.IHttpModule 接口的類都必須實現這個函數。
同時大家看到的OnlineUsers.ResetOnlineList()方法主要是用於“重置(複位)在線表”,
而有關“在線用戶”的功能我會在以後專門寫文章來加以介紹,所以這裏大家隻要知道它要幹的
事(代碼如下)即可:)
/// <summary>2
/// 複位在線表, 如果係統未重啟, 僅是應用程序重新啟動, 則不會重新創建3
/// </summary>4
/// <returns></returns>5
public static int ResetOnlineList()6
{7
try8
{9
// 取得在線表最後一條記錄的tickcount字段 (因為本功能不要求特別精確)10
//int tickcount = DatabaseProvider.GetInstance().GetLastTickCount();11
// 如果距離現在係統運行時間小於10分鍾12
if (System.Environment.TickCount < 600000)13
{14
return InitOnlineList();15
}16
return -1;17
}18
catch19
{20
try21
{22
return InitOnlineList();23
}24
catch25
{26
return -1;27
}28
}29

30
}31

32
而緊隨其後的事件綁定代碼就是一個關鍵點了,形如:
context.BeginRequest += new EventHandler(ReUrl_BeginRequest);
因為當通過瀏覽器提交請求時,IIS都會接管請求進行相應的操作(詳見DUDU的文章:" ASP.NET 2.0運行時
簡要分析")後,最終通過Web.config中的相應配置節(上圖所示)來執行用戶預定的處理操作。而我們的代碼就
在ReUrl_BeginRequest事件中(代碼如下,詳情見注釋):
/// <summary>2
/// 重寫Url3
/// </summary>4
/// <param name="sender">事件的源</param>5
/// <param name="e">包含事件數據的 EventArgs</param>6
private void ReUrl_BeginRequest(object sender, EventArgs e)7
{8
//獲取基本配置信息9
BaseConfigInfo baseconfig = BaseConfigProvider.Instance();10
11
if (baseconfig == null)12
{13
return;14
}15

16
//得到論壇配置信息17
GeneralConfigInfo config = GeneralConfigs.GetConfig();18
HttpContext context = ((HttpApplication)sender).Context;19
string forumPath = baseconfig.Forumpath.ToLower();20

21
string requestPath = context.Request.Path.ToLower();22

23
if (requestPath.StartsWith(forumPath))24
{25
if (requestPath.Substring(forumPath.Length).IndexOf("/") == -1)26
{27
// 聲明並設置默認論壇模板28
string strTemplateid = config.Templateid.ToString();29
// 判斷COOKIE中模板是否是係統當前的有效(已入庫)模板30
if (Utils.InArray(Utils.GetCookie(Utils.GetTemplateCookieName()), 31
Templates.GetValidTemplateIDList()))32
{33
strTemplateid = Utils.GetCookie(Utils.GetTemplateCookieName());34
}35

36
//當訪問的是首頁時37
if (requestPath.EndsWith("/index.aspx"))38
{39
//當配置文件中未指定首頁時,則將forumindex.aspx做為首頁並重寫路徑40
if (config.Indexpage == 0)41
{42


43
context.RewritePath(forumPath + "aspx/" + strTemplateid + "/forumindex.aspx");44
}45
else //否則使用聚合首頁來做為網站首頁並重寫路徑46
{47


48
context.RewritePath(forumPath + "aspx/" + strTemplateid + "/website.aspx");49
}50

51
return;52
}53

54

55
//當使用偽aspx, 如:showforum-1.aspx等.56
if (config.Aspxrewrite == 1)57
{58
//獲取後台設置的可以使用的偽aspx設置.59
//SiteUrls.GetSiteUrls()類和方法說明見下文60
foreach (SiteUrls.URLRewrite url in SiteUrls.GetSiteUrls().Urls)61
{62
//進行正則匹配,來看訪問頁麵是否是用戶定義的偽URL地址63
if (Regex.IsMatch(requestPath, url.Pattern, Utils.GetRegexCompiledOptions() | RegexOptions.IgnoreCase))64
{65
string newUrl = Regex.Replace(requestPath.Substring(context.Request.Path.LastIndexOf("/")), url.Pattern, url.QueryString, Utils.GetRegexCompiledOptions() | RegexOptions.IgnoreCase);66


67
context.RewritePath(forumPath + "aspx/" + strTemplateid + url.Page, string.Empty, newUrl);68
return;69
}70
}71
}72

73


74
75
context.RewritePath(forumPath + "aspx/" + strTemplateid + requestPath.Substring(context.Request.Path.LastIndexOf("/")), string.Empty, context.Request.QueryString.ToString());76
}77
//當前請求路徑是“論壇路徑/archiver(簡潔版路徑)/"下時.78
else if (requestPath.StartsWith(forumPath + "archiver/"))79
{80
81


82
return;83
}84
//當前請求路徑是“論壇路徑/tools/(論壇工具頁麵如:rss,sitemap,help等)”請求時85
else if (requestPath.StartsWith(forumPath + "tools/"))86
{87


88
return;89
}90

91
}92
}93

94
這樣就實現了偽URL的動態轉換(地址重寫)。而相應的“SiteUrls”類則是對偽URL設置信息
進行訪問讀取的“封裝類”,目的就是要將Discuz.Web項目中config文件夾下的urls.config文件轉換
成可訪問的信息對象。形如:
/// <summary>2
/// 站點偽Url信息類3
/// </summary>4
public class SiteUrls5
{6
內部屬性和方法66

67
//獲取偽URL設置對象的實例68
public static SiteUrls GetSiteUrls()69
{70
if (instance == null)71
{72
lock (lockHelper)73
{74
if (instance == null)75
{76
instance = new SiteUrls();77
}78
}79
}80
return instance;81

82
}83

84
public static void SetInstance(SiteUrls anInstance)85
{86
if (anInstance != null)87
instance = anInstance;88
}89

90
public static void SetInstance()91
{92
SetInstance(new SiteUrls());93
}94

95
96
/// <summary>97
/// 重寫偽地址(內部類),用於聲明和封裝對象98
/// </summary>99
public class URLRewrite100
{101
成員變量 154

155
構造函數164
}165

166
}167

168
因為代碼很簡單,這裏就不再另行說明了。
到目前,我們知道了要通過“urls.config”文件來進行偽URL重寫,那麼這個文件中的數據到底是什麼
樣子呢,下麵就是在成功安裝discuz!nt 2.0產品之後的文件內容:
<?xml version="1.0" encoding="utf-8" ?>
<urls>
<rewrite name="showforum"
path="/showforum-{0}-{1}.aspx"
pattern = "/showforum-(/d+)(-(/d+))?.aspx"
page="/showforum.aspx"
querystring="forumid=$1^page=$3" />
<rewrite name="showtopic"
path="/showtopic-{0}-{1}.aspx"
pattern = "/showtopic-(/d+)(-(/d+))?.aspx"
page="/showtopic.aspx"
querystring="topicid=$1^page=$3" />
<rewrite name="userinfo"
path="/userinfo-{0}.aspx"
pattern = "/userinfo-(/d+)*.aspx"
page="/userinfo.aspx"
querystring="userid=$1" />
<rewrite name="rss"
path="/rss-{0}.aspx"
pattern = "/rss-(/d+)?.aspx"
page="/rss.aspx"
querystring="forumid=$1" />
<rewrite name="spacerss"
path="/spacerss-{0}.aspx"
pattern="/spacerss(-(/d+))?.aspx(/?.*?)?"
page="/rss.aspx"
querystring="uid=$2&type=space" />
<rewrite name="photorss"
path="/photorss-{0}.aspx"
pattern="/photorss(-(/d+))?.aspx(/?.*?)?"
page="/rss.aspx"
querystring="uid=$2&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模塊的正常運行了。因為我手上這方麵的實例也不多,所以隻能在這裏聊聊我本
人的一些看法,相信隨著官方“整合案例” 的增加,這方麵的資料會得到更大的補充。
最後更新:2017-04-02 03:42:39