閱讀297 返回首頁    go 技術社區[雲棲]


詳談關於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>

 4 public class HttpModule : System.Web.IHttpModule
 5 {
 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()方法主要是用於“重置(複位)在線表”,
而有關“在線用戶”的功能我會在以後專門寫文章來加以介紹,所以這裏大家隻要知道它要幹的
事(代碼如下)即可:)

 

 1  /// <summary>
 2  /// 複位在線表, 如果係統未重啟, 僅是應用程序重新啟動, 則不會重新創建
 3  /// </summary>
 4  /// <returns></returns>

 5  public static int ResetOnlineList()
 6  {
 7   try
 8   {
 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   catch
19   {
20    try
21    {
22     return InitOnlineList();
23    }

24    catch
25    {
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事件中(代碼如下,詳情見注釋):

 

 1/// <summary>
 2        /// 重寫Url
 3        /// </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文件轉換
成可訪問的信息對象。形如:

 

  1/// <summary>
  2 /// 站點偽Url信息類
  3 /// </summary>

  4 public class SiteUrls
  5 {
  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 URLRewrite
100  {
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&amp;type=space" />
     <rewrite name="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模塊的正常運行了。因為我手上這方麵的實例也不多,所以隻能在這裏聊聊我本
人的一些看法,相信隨著官方“整合案例” 的增加,這方麵的資料會得到更大的補充。

最後更新:2017-04-02 03:42:39

  上一篇:go 數據庫訪問類(直接執行數據庫操作)
  下一篇:go C#啟動停止SQL數據庫服務