技巧百问(10):UrlRewrite以及二级UrlRewrite
这个系列已经很久没写了,因为太懒了。今天想起还是写一些东西,虽然这个用了很久了,但是也可以再看看。
之前在转贴中说到了UrlRewrite这一技术,这个技术的实质就是通过获取当前Url地址匹配对应的规则,传递给真实的访问页面。而这一技术带来的是相对的安全以及访问的便利。从我个人的理解来看,就是让人可以通过简单的方便的方式来访问,对一部分人来说是不让其知道具体的真实页面。
从转贴中了解到,要实现这个功能要么自定义HTTP模块类,要么通过ISAPI来实现,相对来说前者更具有通用性以及扩展性。
要实现自定义HTTP模块类则要实现接口IHttpModule,它其中有Init方法,同时要实现Init中参数HttpApplication的AuthenticateRequest事件,以及我们定义的Rewrite的抽象方法。
上面定义了重写的抽象方法,现在我就要来实现这个重写方法,流程则是读取xml或者Config中对应的正则来匹配获取的Url地址,然后传递到真实的地址,所以我就还要实现几个方法,第一个是读取Config或Xml中对应的正则以及真实地址的方法,第二个是对读取出来的数据分离出正则和真实地址,第三个为了方便在重写的时候快速的对其匹配对第二个分离的数据进行集合。
按照取Url-->匹配正则-->传递地址,这样的过程实现。
<configSections>
<section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter.Config" />
</configSections>
<httpModules>
<add type="URLRewriter.ModuleRewriter, URLRewriter" name="ModuleRewriter" />
</httpModules>
正则匹配为
<RewriterConfig>
<Rules>
<!--一般重写正则-->
<RewriterRule>
<LookFor>~/(\d+)\.aspx</LookFor>
<SendTo>~/Default.aspx?theid=$1</SendTo>
</RewriterRule>
<!--二级重写正则-->
<!--<RewriterRule>
<LookFor>http://(\d+)\.abc.com/</LookFor>
<SendTo>/Default.aspx?theid=$1</SendTo>
</RewriterRule>-->
</Rules>
</RewriterConfig>
到这里重写就完成了,而其他重写都是在这些基础之上,应该可以通过组合之类的实现多种重写。以上的东西希望可以让你清晰的了解UrlRewrite的流程和原理。
参考:
MSDN
之前在转贴中说到了UrlRewrite这一技术,这个技术的实质就是通过获取当前Url地址匹配对应的规则,传递给真实的访问页面。而这一技术带来的是相对的安全以及访问的便利。从我个人的理解来看,就是让人可以通过简单的方便的方式来访问,对一部分人来说是不让其知道具体的真实页面。
从转贴中了解到,要实现这个功能要么自定义HTTP模块类,要么通过ISAPI来实现,相对来说前者更具有通用性以及扩展性。
要实现自定义HTTP模块类则要实现接口IHttpModule,它其中有Init方法,同时要实现Init中参数HttpApplication的AuthenticateRequest事件,以及我们定义的Rewrite的抽象方法。
public virtual void Init(HttpApplication app)
{
app.AuthorizeRequest += new EventHandler(this.BaseModuleRewriter_AuthorizeRequest);
}
public virtual void Dispose() { }
protected virtual void BaseModuleRewriter_AuthorizeRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
///一般重写
Rewrite(app.Request.Path, app);
///二级域名重写
//Rewrite(app.Request.Url.AbsoluteUri, app);
}
protected abstract void Rewrite(string requestedPath, HttpApplication app);
其中注释掉的部分为二级域名重写的方法,唯一的区别就是获取的地址为绝对地址,因为一般重写即是:www.abc.com/123/实际访问为www.abc.com/index.aspx?id=123,而二级域名重写则是:123.abc.com实际访问为www.abc.com/index.aspx?id=123。这两个虽然结果是一样的,但是他们的实现方法是不同的。要实现二级域名重写首先就是要域名支持泛解析,所谓泛解析就是不论是123.abc.com、abc.com、abc123.abc.com都访问的是www.abc.com,这个也是二级域名实现的基础。因为在传递真实的地址的时候就是通过泛解析来让地址进入www.abc.com再加上后面的index.aspx?id=123。{
app.AuthorizeRequest += new EventHandler(this.BaseModuleRewriter_AuthorizeRequest);
}
public virtual void Dispose() { }
protected virtual void BaseModuleRewriter_AuthorizeRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
///一般重写
Rewrite(app.Request.Path, app);
///二级域名重写
//Rewrite(app.Request.Url.AbsoluteUri, app);
}
protected abstract void Rewrite(string requestedPath, HttpApplication app);
上面定义了重写的抽象方法,现在我就要来实现这个重写方法,流程则是读取xml或者Config中对应的正则来匹配获取的Url地址,然后传递到真实的地址,所以我就还要实现几个方法,第一个是读取Config或Xml中对应的正则以及真实地址的方法,第二个是对读取出来的数据分离出正则和真实地址,第三个为了方便在重写的时候快速的对其匹配对第二个分离的数据进行集合。
1 //以下代码为读取数据,并加入到集合中
2 public class RewriterConfigSerializerSectionHandler : IConfigurationSectionHandler
3 {
4 public object Create(object parent, object configContext, System.Xml.XmlNode section)
5 {
6 XmlSerializer ser = new XmlSerializer(typeof(RewriterConfiguration));
7
8 return ser.Deserialize(new XmlNodeReader(section));
9 }
10
11 [Serializable()]
12 [XmlRoot("RewriterConfig")]//这里可以随便定义,主要通过这个名字的节点来获取正则匹配。cxbkkk
13 public class RewriterConfiguration
14 {
15
16 private RewriterRuleCollection rules;
17 public static RewriterConfiguration GetConfig()
18 {
19 if (HttpContext.Current.Cache["RewriterConfig"] == null)
20 HttpContext.Current.Cache.Insert("RewriterConfig", ConfigurationSettings.GetConfig("RewriterConfig"));
21
22 return (RewriterConfiguration)HttpContext.Current.Cache["RewriterConfig"];
23 }
24
25 #region Public Properties
26 /// <summary>
27 /// 正则匹配集合.
28 /// </summary>
29 public RewriterRuleCollection Rules
30 {
31 get
32 {
33 return rules;
34 }
35 set
36 {
37 rules = value;
38 }
39 }
40 #endregion
41 }
42
43 public class RewriterRule
44 {
45 private string lookFor, sendTo;
46
47 public string LookFor
48 {
49 get
50 {
51 return lookFor;
52 }
53 set
54 {
55 lookFor = value;
56 }
57 }
58
59
60 public string SendTo
61 {
62 get
63 {
64 return sendTo;
65 }
66 set
67 {
68 sendTo = value;
69 }
70 }
71
72 }
73
74 [Serializable()]
75 public class RewriterRuleCollection : CollectionBase
76 {
77 /// <summary>
78 /// 加入一个新匹配到集合.
79 /// </summary>
80
81 public virtual void Add(RewriterRule r)
82 {
83 this.InnerList.Add(r);
84 }
85
86 /// <summary>
87 /// 集合的索引.
88 /// </summary>
89 public RewriterRule this[int index]
90 {
91 get
92 {
93 return (RewriterRule)this.InnerList[index];
94 }
95 set
96 {
97 this.InnerList[index] = value;
98 }
99 }
100 }
101
102 }
上面的我统一命名空间为Rewrite.config,我们还要实现匹配后传递地址的过程。2 public class RewriterConfigSerializerSectionHandler : IConfigurationSectionHandler
3 {
4 public object Create(object parent, object configContext, System.Xml.XmlNode section)
5 {
6 XmlSerializer ser = new XmlSerializer(typeof(RewriterConfiguration));
7
8 return ser.Deserialize(new XmlNodeReader(section));
9 }
10
11 [Serializable()]
12 [XmlRoot("RewriterConfig")]//这里可以随便定义,主要通过这个名字的节点来获取正则匹配。cxbkkk
13 public class RewriterConfiguration
14 {
15
16 private RewriterRuleCollection rules;
17 public static RewriterConfiguration GetConfig()
18 {
19 if (HttpContext.Current.Cache["RewriterConfig"] == null)
20 HttpContext.Current.Cache.Insert("RewriterConfig", ConfigurationSettings.GetConfig("RewriterConfig"));
21
22 return (RewriterConfiguration)HttpContext.Current.Cache["RewriterConfig"];
23 }
24
25 #region Public Properties
26 /// <summary>
27 /// 正则匹配集合.
28 /// </summary>
29 public RewriterRuleCollection Rules
30 {
31 get
32 {
33 return rules;
34 }
35 set
36 {
37 rules = value;
38 }
39 }
40 #endregion
41 }
42
43 public class RewriterRule
44 {
45 private string lookFor, sendTo;
46
47 public string LookFor
48 {
49 get
50 {
51 return lookFor;
52 }
53 set
54 {
55 lookFor = value;
56 }
57 }
58
59
60 public string SendTo
61 {
62 get
63 {
64 return sendTo;
65 }
66 set
67 {
68 sendTo = value;
69 }
70 }
71
72 }
73
74 [Serializable()]
75 public class RewriterRuleCollection : CollectionBase
76 {
77 /// <summary>
78 /// 加入一个新匹配到集合.
79 /// </summary>
80
81 public virtual void Add(RewriterRule r)
82 {
83 this.InnerList.Add(r);
84 }
85
86 /// <summary>
87 /// 集合的索引.
88 /// </summary>
89 public RewriterRule this[int index]
90 {
91 get
92 {
93 return (RewriterRule)this.InnerList[index];
94 }
95 set
96 {
97 this.InnerList[index] = value;
98 }
99 }
100 }
101
102 }
按照取Url-->匹配正则-->传递地址,这样的过程实现。
1 protected override void Rewrite(string requestedPath, System.Web.HttpApplication app)
2 {
3 RewriterRuleCollection rules = RewriterConfiguration.GetConfig().Rules;
4
5 for (int i = 0; i < rules.Count; i++)
6 {
7 ///一般重写
8 string lookFor = "^" + RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, rules[i].LookFor) + "$";
9 ///二级域名重写
10 //string lookFor = "^" + rules[i].LookFor + "$";
11
12 Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);
13
14 if (re.IsMatch(requestedPath))
15 {
16 string sendToUrl = RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, re.Replace(requestedPath, rules[i].SendTo));
17
18 app.Context.Trace.Write("ModuleRewriter", "Rewriting URL to " + sendToUrl);
19
20 RewriterUtils.RewriteUrl(app.Context, sendToUrl);
21 break; }
22 }
23
24 app.Context.Trace.Write("ModuleRewriter", "Exiting ModuleRewriter");
25 }
在匹配的时候就涉及到了使用什么样的地址来匹配,二级域名因为使用的绝对地址,所以这里就不需要通过我们定义的方法Resolve来补充相对地址。现在剩下的就是RewriteUtils中的地址补充以及地址传递。2 {
3 RewriterRuleCollection rules = RewriterConfiguration.GetConfig().Rules;
4
5 for (int i = 0; i < rules.Count; i++)
6 {
7 ///一般重写
8 string lookFor = "^" + RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, rules[i].LookFor) + "$";
9 ///二级域名重写
10 //string lookFor = "^" + rules[i].LookFor + "$";
11
12 Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);
13
14 if (re.IsMatch(requestedPath))
15 {
16 string sendToUrl = RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, re.Replace(requestedPath, rules[i].SendTo));
17
18 app.Context.Trace.Write("ModuleRewriter", "Rewriting URL to " + sendToUrl);
19
20 RewriterUtils.RewriteUrl(app.Context, sendToUrl);
21 break; }
22 }
23
24 app.Context.Trace.Write("ModuleRewriter", "Exiting ModuleRewriter");
25 }
1 internal static void RewriteUrl(HttpContext context, string sendToUrl)
2 {
3 string x, y;
4 RewriteUrl(context, sendToUrl, out x, out y);
5 }
6
7 internal static void RewriteUrl(HttpContext context, string sendToUrl, out string sendToUrlLessQString, out string filePath)
8 {
9
10 if (context.Request.QueryString.Count > 0)
11 {
12 if (sendToUrl.IndexOf('?') != -1)
13 sendToUrl += "&" + context.Request.QueryString.ToString();
14 else
15 sendToUrl += "?" + context.Request.QueryString.ToString();
16 }
17
18 string queryString = String.Empty;
19 sendToUrlLessQString = sendToUrl;
20 if (sendToUrl.IndexOf('?') > 0)
21 {
22 sendToUrlLessQString = sendToUrl.Substring(0, sendToUrl.IndexOf('?'));
23 queryString = sendToUrl.Substring(sendToUrl.IndexOf('?') + 1);
24 }
25
26 filePath = string.Empty;
27 filePath = context.Server.MapPath(sendToUrlLessQString);
28
29 context.RewritePath(sendToUrlLessQString, String.Empty, queryString);
30 }
31
32 internal static string ResolveUrl(string appPath, string url)
33 {
34 if (url.Length == 0 || url[0] != '~')
35 return url;
36 else
37 {
38 if (url.Length == 1)
39 return appPath;
40 if (url[1] == '/' || url[1] == '\\')
41 {
42 if (appPath.Length > 1)
43 return appPath + "/" + url.Substring(2);
44 else
45 return "/" + url.Substring(2);
46 }
47 else
48 {
49 if (appPath.Length > 1)
50 return appPath + "/" + url.Substring(1);
51 else
52 return appPath + url.Substring(1);
53 }
54 }
55 }
然后就是对配置文件或Xml中添加访问模块的名称,以及以读取中定义的名称为节点的正则匹配和传递地址。2 {
3 string x, y;
4 RewriteUrl(context, sendToUrl, out x, out y);
5 }
6
7 internal static void RewriteUrl(HttpContext context, string sendToUrl, out string sendToUrlLessQString, out string filePath)
8 {
9
10 if (context.Request.QueryString.Count > 0)
11 {
12 if (sendToUrl.IndexOf('?') != -1)
13 sendToUrl += "&" + context.Request.QueryString.ToString();
14 else
15 sendToUrl += "?" + context.Request.QueryString.ToString();
16 }
17
18 string queryString = String.Empty;
19 sendToUrlLessQString = sendToUrl;
20 if (sendToUrl.IndexOf('?') > 0)
21 {
22 sendToUrlLessQString = sendToUrl.Substring(0, sendToUrl.IndexOf('?'));
23 queryString = sendToUrl.Substring(sendToUrl.IndexOf('?') + 1);
24 }
25
26 filePath = string.Empty;
27 filePath = context.Server.MapPath(sendToUrlLessQString);
28
29 context.RewritePath(sendToUrlLessQString, String.Empty, queryString);
30 }
31
32 internal static string ResolveUrl(string appPath, string url)
33 {
34 if (url.Length == 0 || url[0] != '~')
35 return url;
36 else
37 {
38 if (url.Length == 1)
39 return appPath;
40 if (url[1] == '/' || url[1] == '\\')
41 {
42 if (appPath.Length > 1)
43 return appPath + "/" + url.Substring(2);
44 else
45 return "/" + url.Substring(2);
46 }
47 else
48 {
49 if (appPath.Length > 1)
50 return appPath + "/" + url.Substring(1);
51 else
52 return appPath + url.Substring(1);
53 }
54 }
55 }
<configSections>
<section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter.Config" />
</configSections>
<httpModules>
<add type="URLRewriter.ModuleRewriter, URLRewriter" name="ModuleRewriter" />
</httpModules>
正则匹配为
<RewriterConfig>
<Rules>
<!--一般重写正则-->
<RewriterRule>
<LookFor>~/(\d+)\.aspx</LookFor>
<SendTo>~/Default.aspx?theid=$1</SendTo>
</RewriterRule>
<!--二级重写正则-->
<!--<RewriterRule>
<LookFor>http://(\d+)\.abc.com/</LookFor>
<SendTo>/Default.aspx?theid=$1</SendTo>
</RewriterRule>-->
</Rules>
</RewriterConfig>
到这里重写就完成了,而其他重写都是在这些基础之上,应该可以通过组合之类的实现多种重写。以上的东西希望可以让你清晰的了解UrlRewrite的流程和原理。
参考:
MSDN