在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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
2
/// 论坛HttpModule类
3
/// </summary> 4
public class HttpModule : System.Web.IHttpModule
5![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
7
/// 实现接口的Init方法
8
/// </summary>
9
/// <param name="context"></param>
10
public void Init(HttpApplication context)
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
12
OnlineUsers.ResetOnlineList();
13
context.BeginRequest += new EventHandler(ReUrl_BeginRequest);
14
}
15![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
17
18![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
上面代码中的Init(HttpApplication context)是HttpModule类进行操作的入口,所有实现
System.Web.IHttpModule 接口的类都必须实现这个函数。
同时大家看到的OnlineUsers.ResetOnlineList()方法主要是用于“重置(复位)在线表”,
而有关“在线用户”的功能我会在以后专门写文章来加以介绍,所以这里大家只要知道它要干的
事(代码如下)即可:)
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
2
/// 复位在线表, 如果系统未重启, 仅是应用程序重新启动, 则不会重新创建
3
/// </summary>
4
/// <returns></returns> 5
public static int ResetOnlineList()
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
7
try
8![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
9
// 取得在线表最后一条记录的tickcount字段 (因为本功能不要求特别精确)
10
//int tickcount = DatabaseProvider.GetInstance().GetLastTickCount();
11
// 如果距离现在系统运行时间小于10分钟
12
if (System.Environment.TickCount < 600000)
13![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
14
return InitOnlineList();
15
}
16
return -1;
17
}
18
catch
19![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
20
try
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
22
return InitOnlineList();
23
}
24
catch
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
26
return -1;
27
}
28
}
29![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
30
}
31![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
32![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
而紧随其后的事件绑定代码就是一个关键点了,形如:
context.BeginRequest += new EventHandler(ReUrl_BeginRequest);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
因为当通过浏览器提交请求时,IIS都会接管请求进行相应的操作(详见DUDU的文章:" ASP.NET 2.0运行时
简要分析")后,最终通过Web.config中的相应配置节(上图所示)来执行用户预定的处理操作。而我们的代码就
在ReUrl_BeginRequest事件中(代码如下,详情见注释):
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
8
//获取基本配置信息
9
BaseConfigInfo baseconfig = BaseConfigProvider.Instance();
10
11
if (baseconfig == null)
12![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
13
return;
14
}
15![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
//得到论坛配置信息
17
GeneralConfigInfo config = GeneralConfigs.GetConfig();
18
HttpContext context = ((HttpApplication)sender).Context;
19
string forumPath = baseconfig.Forumpath.ToLower();
20![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
21
string requestPath = context.Request.Path.ToLower();
22![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
23
if (requestPath.StartsWith(forumPath))
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
25
if (requestPath.Substring(forumPath.Length).IndexOf("/") == -1)
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
27
// 声明并设置默认论坛模板
28
string strTemplateid = config.Templateid.ToString();
29
// 判断COOKIE中模板是否是系统当前的有效(已入库)模板
30
if (Utils.InArray(Utils.GetCookie(Utils.GetTemplateCookieName()),
31
Templates.GetValidTemplateIDList()))
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
33
strTemplateid = Utils.GetCookie(Utils.GetTemplateCookieName());
34
}
35![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
36
//当访问的是首页时
37
if (requestPath.EndsWith("/index.aspx"))
38![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
39
//当配置文件中未指定首页时,则将forumindex.aspx做为首页并重写路径
40
if (config.Indexpage == 0)
41![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
42
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
43
context.RewritePath(forumPath + "aspx/" + strTemplateid + "/forumindex.aspx");
44
}
45
else //否则使用聚合首页来做为网站首页并重写路径
46![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
47
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
48
context.RewritePath(forumPath + "aspx/" + strTemplateid + "/website.aspx");
49
}
50![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
51
return;
52
}
53![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
54![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
55
//当使用伪aspx, 如:showforum-1.aspx等.
56
if (config.Aspxrewrite == 1)
57![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
58
//获取后台设置的可以使用的伪aspx设置.
59
//SiteUrls.GetSiteUrls()类和方法说明见下文
60
foreach (SiteUrls.URLRewrite url in SiteUrls.GetSiteUrls().Urls)
61![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
62
//进行正则匹配,来看访问页面是否是用户定义的伪URL地址
63
if (Regex.IsMatch(requestPath, url.Pattern, Utils.GetRegexCompiledOptions() | RegexOptions.IgnoreCase))
64![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
65
string newUrl = Regex.Replace(requestPath.Substring(context.Request.Path.LastIndexOf("/")), url.Pattern, url.QueryString, Utils.GetRegexCompiledOptions() | RegexOptions.IgnoreCase);
66
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
67
context.RewritePath(forumPath + "aspx/" + strTemplateid + url.Page, string.Empty, newUrl);
68
return;
69
}
70
}
71
}
72![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
73
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
80
81
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
82
return;
83
}
84
//当前请求路径是“论坛路径/tools/(论坛工具页面如:rss,sitemap,help等)”请求时
85
else if (requestPath.StartsWith(forumPath + "tools/"))
86![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
87
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
88
return;
89
}
90![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
91
}
92
}
93![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
94![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这样就实现了伪URL的动态转换(地址重写)。而相应的“SiteUrls”类则是对伪URL设置信息
进行访问读取的“封装类”,目的就是要将Discuz.Web项目中config文件夹下的urls.config文件转换
成可访问的信息对象。形如:
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
2
/// 站点伪Url信息类
3
/// </summary> 4
public class SiteUrls
5![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
6![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
内部属性和方法#region 内部属性和方法
7
private static object lockHelper = new object();
8
private static volatile SiteUrls instance = null;
9![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
14
get
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
16
return _Urls;
17
}
18
set
19![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
20
_Urls = value;
21
}
22
}
23![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
24
private System.Collections.Specialized.NameValueCollection _Paths;
25
public System.Collections.Specialized.NameValueCollection Paths
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
27
get
28![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
29
return _Paths;
30
}
31
set
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
33
_Paths = value;
34
}
35
}
36
37
private SiteUrls()
38![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
39
Urls = new System.Collections.ArrayList();
40
Paths = new System.Collections.Specialized.NameValueCollection();
41![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
42
XmlDocument xml = new XmlDocument();
43![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
44
xml.Load(SiteUrlsFile);
45![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
46
XmlNode root = xml.SelectSingleNode("urls");
47
foreach(XmlNode n in root.ChildNodes)
48![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
49
if (n.NodeType != XmlNodeType.Comment && n.Name.ToLower() == "rewrite")
50![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
57
if (name != null && path != null && page != null && querystring != null && pattern != null)
58![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
67
//获取伪URL设置对象的实例
68
public static SiteUrls GetSiteUrls()
69![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
70
if (instance == null)
71![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
72
lock (lockHelper)
73![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
74
if (instance == null)
75![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
76
instance = new SiteUrls();
77
}
78
}
79
}
80
return instance;
81![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
82
}
83![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
84
public static void SetInstance(SiteUrls anInstance)
85![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
86
if (anInstance != null)
87
instance = anInstance;
88
}
89![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
90
public static void SetInstance()
91![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
92
SetInstance(new SiteUrls());
93
}
94![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
95
96![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
97
/// 重写伪地址(内部类),用于声明和封装对象
98
/// </summary>
99
public class URLRewrite
100![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
101![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
成员变量#region 成员变量
102
private string _Name;
103
public string Name
104![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
105
get
106![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
107
return _Name;
108
}
109
set
110![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
111
_Name = value;
112
}
113
}
114![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
115
private string _Pattern;
116
public string Pattern
117![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
118
get
119![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
120
return _Pattern;
121
}
122
set
123![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
124
_Pattern = value;
125
}
126
}
127![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
128
private string _Page;
129
public string Page
130![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
131
get
132![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
133
return _Page;
134
}
135
set
136![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
137
_Page = value;
138
}
139
}
140![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
141
private string _QueryString;
142
public string QueryString
143![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
144
get
145![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
146
return _QueryString;
147
}
148
set
149![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
150
_QueryString = value;
151
}
152
}
153
#endregion
154![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
155![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
构造函数#region 构造函数
156
public URLRewrite(string name, string pattern, string page, string querystring)
157![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
158
_Name = name;
159
_Pattern = pattern;
160
_Page = page;
161
_QueryString = querystring;
162
}
163
#endregion
164
}
165![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
166
}
167![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
168![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
因为代码很简单,这里就不再另行说明了。
到目前,我们知道了要通过“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>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
大家看到了吧。就目前而言伪URL重写的页面(page节点)包括如下几个:
showforum.aspx(版块列表),showtopic.aspx(主题列表),userinfo.aspx(用户信息),rss.aspx,
虽然不多,但已基本满足了设置需要。
而设置(配置)管理的页面在后台的“全局”-->“常规选项”-->“基本设置”-->“编辑伪静态url替换
规则”,如果所示:
![](https://images.cnblogs.com/cnblogs_com/daizhj/url.config_set.jpg)
相信聊到这里大家对我们产品的这项功能应该有个大概认识了,但这不并是全部的设置,因为如果要接管
形如"showforum-1-2.htm"这样的页面,是必须要到IIS里去“搞一把”的。因为默认的IIS中对htm(html)
的处理是无法实现将上面的链接转成“showforum.aspx?forumid=1&page=2”的形式的,所以这里要参考
一下官方文档(使用伪Url地址)中的方法,相应图示如下:
![](http://nt.discuz.net/doc/images/setup/UrlReWriter.gif)
![](http://nt.discuz.net/doc/images/setup/UrlReWriterIni.gif)
好的,下面再接着聊一下关于与第三方产品“整和”的问题。因为目前大多数采用 .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,地址重写