|
在ASP.NET中实现Url Rewriting-3
1.12. 使用网址重写引擎实现简单的网址重写 为了便于在Web应用程序中实现网址重写,我构建了一个网址重写引擎,该引擎提供下列功能: 可以在web.config文件中为页面开发者定义其所使用的网址重写引擎的规则; 通过使用正则表达式来使所制定的网址重写规则具有更加强大的重写能力; 能够通过简单配置即可在HttpModule和HttpHandler中使用网址重写。 本节只探讨通过HttpModule来实现网址重写,要了解如何通过HttpHandler来实现网址重写请下载本文提供的代码。
1.12.1. 设置网址重写引擎的配置信息 我们来探讨一下在web.config中网址重写规则的配置节。首先必须在web.config文件中指出是否需要在HttpHandler或者HttpModule中调用网址重写,在web.config中,下文已经包含了两个已经被注释掉的配置节:
<!--
<HttpModules>
<add type="URLRewriter.ModuleRewriter,URLRewriter" name="ModuleRewriter"/>
</HttpModules>
-->

<!--
<httpHandlers>
<add verb="*" path="*.aspx" type="URLRewriter.RewriterFactoryHandler,URLRewriter" />
</httpHandlers>
-->

被注释掉的<HttpModules>为配置使用HttpModule调用网址重写;注释掉的<httpHandler>为配置使用HttpHandler调用网址重写。 不论配置使用<HttpModules>还是<httpHandlers>调用网址重写,除此之外还须配置网址重写规则,一条重写规则包括两项字符串:请求URL中的查找模式和针对该模式的匹配成功后的替换字符串。该信息在web.config文件中用下列标签描述:
<RewriterConfig>
<Rules>
<RewriterRule>
<LookFor>pattern to look for</LookFor>
<SendTo>String to replace pattern with </SendTo>
</RewriterRule>
<RewriterRule>
<LookFor>pattern to look for</LookFor>
<SendTo>String to replace pattern with </SendTo>
</RewriterRule>

</Rules>
</RewriterConfig>

每一条规则都用一个<RewriterRule>元素表示,以<LookFor>节表示查询模式,当查询模式发现匹配字符串时便用<SendTo>节表示的字符串进行替换。这些规则从上到下进行查询匹配,如果找到一个匹配则按此规则执行网址重写,并且停止查找。 配置<LookFor>节要使用正则表达式来进行字符串匹配和替换。(在此我们举一个例子来说明如何使用正则表达式来对字符串进行匹配和替换。)既然该查找模式是一个正则表达式,那么要注意避开对正则表达式保留字符串的直接使用。(正则表达式的保留字符串包括有:.,?,^,$,等等,可以通过在前面加上一个反斜线来引用这些保留字符,例如\.表示引用一个句点)
1.12.2. 使用HttpModule来执行网址重写
创建一个HttpModule很简单,只要创建一个实现IHttpModule接口的类,该IHttpModule接口定义了两个方法: Init(HttpApplication),该方法在HttpModule初始化时引发,通过该方法为HttpApplication事件调用相应的事件委托; Dispose(),当相应请求处理结束并发送回IIS调用此方法,通过此方法执行最终所有的清理和回收程序。 为了更加方便地为网址重写创建HttpModule,从一开始我就创建一个抽象的基类(BaseModuleRewriter),该类实现了IHttpModule接口。在Init(HttpApplication)事件中,它通过BaseModuleRewriter_AuthorizeRequest方法引发了HttpApplication的AuthorizeRequest事件,该BaseModuleRewriter_AuthorizeRequest方法通过该类的Rewrite()方法重写传入参数HttpApplication对象的内部请求虚拟路径(Path)。在BaseModuleRewriter对象中,该Rewrite()方法是抽象的,并且没有实际内容,但在继承自该类的对象中必须重载Rewrite()方法并为该方法提供实际内容。 通过对该基类的继承,所有需要做的工作就是创建一个继承自BaseModuleRewriter的类,重载Rewrite()方法并在该方法中添加网址重写逻辑代码。下文列出BaseModuleRewriter代码:
public abstract class BaseModuleRewriter : IHttpModule
  {
 public virtual void Init(HttpApplication app) {
// WARNING! This does not work with Windows authentication!
// If you are using Windows authentication,
// change to app.BeginRequest
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);
}

protected abstract void Rewrite(string requestedPath, HttpApplication app);
}
注意:该BaseModuleRewriter类将网址重写放在AuthorizeRequest事件中调用,如果要使用Windows验证并使用文件验证模式时请修改代码将网址授权放在BeginRequest或者AuthenticateRequest事件中。 ModuleRewriter继承自BaseModuleRewriter,并真正意义地实现了网址重写的操作,该类仅包含一个重载了的方法Rewrite(),其内容如下文所示:
protected override void Rewrite(string requestedPath, System.Web.HttpApplication app)
  {
// get the configuration rules
RewriterRuleCollection rules = RewriterConfiguration.GetConfig().Rules;
// iterate through each rule
for(int i = 0; i < rules.Count; i++)
 {
// get the pattern to look for, and
// Resolve the Url (convert ~ into the appropriate directory)
string lookFor = "^" +
RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, rules[i].LookFor) + "$";
// Create a regex (note that IgnoreCase is set )
Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);
// See if a match is found
if (re.IsMatch(requestedPath))
 {
// match found - do any replacement needed
string sendToUrl = RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, re.Replace(requestedPath, rules[i].SendTo));
// Rewrite the URL
RewriterUtils.RewriteUrl(app.Context, sendToUrl);
break; // exit the for loop
}
}
}

该Rewriter()方法以获取web.config文件中的网址重写规则的设置为起始,它通过循环访问各条网址重写规则,每次均获取当前规则中的LookFor属性,用正则表达式验证并判断是否查找是否对当前请求的网址是否有匹配。 如果发现一条匹配,将用当前规则的SendTo值对请求的路径执行一个正则表达式替换,替换后的地址通过参数的形式传给RewriterUtils.RewriteUrl()方法,RewriterUtils是一个帮助类,它提供一对HttpModule和HttpHandler都可以使用的静态方法,RewriterUrl()方法只是简单地调用了HttpContext对象的RewritePath()方法。 注意:你已经注意到了当执行正则表达式匹配和替换的时候调用了一个RewriterUtils.ResolveUrl()方法。该帮助方法简单地替换了应用程序路径中“~”的所有实例。 本文附录中提供所有涉及该网址重写引擎的代码下载,我们已经探讨了主要的部分,但是还有其它一些组件诸如将web.config文件中XML格式化了的网址重写规则反序列化至一个对象的类定义、通过HttpHandlerFactory实现网址重写的类定义等。本文最后三节将通过一些真实案例来探讨网址重写的技术。
1.12.3. 用网址重写引擎实现简单的网址重写 为了更好地示范网址重写引擎的运行,我们来建立一个ASP.NET Web应用程序来实现简单的网址重写引擎。假定我们为一家在线销售各类商品的公司服务,这些产品划分为以下类别: 分类编号(CategoryID) 分类名称(CategoryName) 1 饮料(Beverages) 2 调味品(Condiments) 3 工艺品(Confections) 4 日记本(Diary Products) ... ... 假定已经建立好一个名为ListProductsByCategoryID.aspx的ASP.NET页面文件,它通过查询参数获取一个分类编号,并根据此编号获取所有该分类下的所有商品。如果用户想浏览所销售的饮料类商品可以通过ListProductsByCategoryID.aspx?CategoryID=1来访问,如果用户想浏览所销售的日记本类商品可以通过ListProductsByCategoryID.aspx?CategoryID=4来访问。假定还有一个页面ListCategories.aspx,它列出所有代售商品的分类编号。 显然这里发现了一个网址重写的案例。对于用户来说他们所输入的地址不具有任何实际意义并且不具备任何“隐蔽性”,倒不如使用网址重写引擎让用户去访问/Products/Baverage.aspx地址,系统将该地址重写到ListProductsByCategoryID.aspx?CategoryID=1。我们可以在web.config文件中来完成网址重写任务:
<RewriterConfig>
<Rules>
<!—- Rules for products lister -->
<RewriterRule>
<LookFor>~/Products/Baverage.aspx</LookFor>
<SendTo>~/ListProductsByCategoryID.aspx?CategoryID=1</SendTo>
</RewriterRule>
</Rules>
</RewriterConfig>
很明显地看到,搜索用户访问的路径是否匹配/Products/Baverage.aspx,如果匹配的话,则将网址重写到/ListProductsByCategoryID.aspx?CategoryID=1。 注意:你会发现<LookFor>节点中避免直接在“Baverage.aspx”中使用句点“.”是因为<LookFor>节点的值是正则表达式的匹配模式,在正则表达式中句点符号是一个特殊字符,它表示匹配任何一个字符,也就是说如果访问BaverageQaspx时也会发生匹配,为了避免发生这个句点引起的匹配我们得在该句点符号前面加上一个“\”,表示引用句点符号 通过该规则定义,当用户访问/Products/Baverage.aspx文件的时候,他们将看到代售的饮料类商品列表信息。图3为访问/Products/Baverage.aspx地址时的浏览器截图,注意在浏览器中地址栏上显示的是用户输入的/Products/Baverage.aspx地址,但是实际访问的地址却是网址重写后的/ListProductsByCategoryID.aspx?CategoryID=1。(事实上,在服务器上根本就不存在/Products/Baverage.aspx文件!)
 图三.网址重写后的对商品分类的请求
2006-9-5 9:50:09
Posted by jser | 阅读全文() | 回复(3) | 引用通告() | 编辑
|