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  内部属性和方法#region 内部属性和方法
  7  private static object lockHelper = new object();
  8  private static volatile SiteUrls instance = null;
  9
 10  string SiteUrlsFile = HttpContext.Current.Server.MapPath(BaseConfigs.GetForumPath + "config/urls.config");
 11  private System.Collections.ArrayList _Urls;
 12  public System.Collections.ArrayList Urls
 13  {
 14   get
 15   {
 16    return _Urls;
 17   }

 18   set
 19   {
 20    _Urls = value;
 21   }

 22  }

 23
 24  private System.Collections.Specialized.NameValueCollection _Paths;
 25  public System.Collections.Specialized.NameValueCollection Paths
 26  {
 27   get
 28   {
 29    return _Paths;
 30   }

 31   set
 32   {
 33    _Paths = value;
 34   }

 35  }

 36  
 37  private SiteUrls()
 38  {
 39   Urls = new System.Collections.ArrayList();
 40   Paths = new System.Collections.Specialized.NameValueCollection();
 41
 42   XmlDocument xml = new XmlDocument();
 43
 44   xml.Load(SiteUrlsFile);
 45
 46   XmlNode root = xml.SelectSingleNode("urls");
 47   foreach(XmlNode n in root.ChildNodes)
 48   {
 49    if (n.NodeType != XmlNodeType.Comment && n.Name.ToLower() == "rewrite")
 50    {
 51     XmlAttribute name = n.Attributes["name"];
 52     XmlAttribute path = n.Attributes["path"];
 53     XmlAttribute page = n.Attributes["page"];
 54     XmlAttribute querystring = n.Attributes["querystring"];
 55     XmlAttribute pattern = n.Attributes["pattern"];
 56
 57     if (name != null && path != null && page != null && querystring != null && pattern != null)
 58     {
 59      Paths.Add(name.Value, path.Value);
 60      Urls.Add(new URLRewrite(name.Value, pattern.Value, page.Value.Replace("^""&"), querystring.Value.Replace("^""&")));
 61     }

 62    }

 63   }

 64  }

 65  #endregion

 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   成员变量#region 成员变量
102   private string _Name;
103   public string Name
104   {
105    get
106    {
107     return _Name;
108    }

109    set
110    {
111     _Name = value;
112    }

113   }

114
115   private string _Pattern;
116   public string Pattern
117   {
118    get
119    {
120     return _Pattern;
121    }

122    set
123    {
124     _Pattern = value;
125    }

126   }

127
128   private string _Page;
129   public string Page
130   {
131    get
132    {
133     return _Page;
134    }

135    set
136    {
137     _Page = value;
138    }

139   }

140
141   private string _QueryString;
142   public string QueryString
143   {
144    get
145    {
146     return _QueryString;
147    }

148    set
149    {
150     _QueryString = value;
151    }

152   }

153   #endregion
 
154
155   构造函数#region 构造函数
156   public URLRewrite(string name, string pattern, string page, string querystring)
157   {
158    _Name = name;
159    _Pattern = pattern;
160    _Page = page;
161    _QueryString = querystring;
162   }

163   #endregion

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模块的正常运行了。因为我手上这方面的实例也不多,所以只能在这里聊聊我本
人的一些看法,相信随着官方“整合案例” 的增加,这方面的资料会得到更大的补充。

      好了,今天就先聊到这吧,如果大家有什么问题,欢迎与我交流:)

      我的email:  daizhj@discuz.com,  daizhj617595@126.com, daizhj@gmail.com

      关键字:.net,discuz,disucznt,httpmodule,HttpHandler,RewritePath,地址重写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值