全面解析ASP.NET2.0下的URL重写

本文将通过实例比较ASP.NET下的三种典型URL重写方案——ISAPI重写(使用开源组件IIRF),ASP.NET2.0内置的urlMappings和基于自定义HTTPModule的URL重写(使用NBear.Web中的UrlRewriteModule实现),并探讨URL重写中可能遇到的陷阱及处理办法。

下载示例程序源码

需要手动为UrlRewriteSample目录添加一个到http://localhost/UrlRewriteSample的同名虚拟目录,允许匿名访问,并设置目录默认页为default.aspx。

另外,为了启用IIRF的URL重写支持,需要将UrlRewriteSample/bin目录下的IsapiRewrite4.dll添加为IIS默认网站的ISAPI过滤器。

设置重写规则

注意,我们的演示程序中将混合使用三种方式的URL重写,因此,需要为三种实现分别设置一些URL重写规则:

1、IIRF,对于IIRF,对应于IsapiRewrite4.dll,在相同的目录会有一个IsapiRewrite4.ini文件,除了默认的一些设置,我们在文件末尾添加了几条自定义规则如下:

# Custom RewriteRules
RewriteRule  ^/UrlRewriteSample/test(.*).aspx         /UrlRewriteSample/Default.aspx?page=$1
RewriteRule  ^/UrlRewriteSample/folder/(.*).aspx    /UrlRewriteSample/Default.aspx?folder=$1
RewriteRule  ^/UrlRewriteSample/folder/?               /UrlRewriteSample/Default.aspx?folder=default

熟悉正则表达式的朋友应该很容易理解上面这三条规则。

规则一将形如testXXX.aspx这样的页面访问,重写为Default.aspx?page=XXX这样的页面;
规则二将形如folder/XXX.aspx的路径,重写为Default.aspx?folder=XXX这样的页面;
规则三将不带任何文件的folder目录的访问,重写为Default.aspx?folder=default这样的页面。

2、urlMappings是ASP.NET2.0内置支持的URL重写配置块,它应该包含在web.config的<system.web>配置块中。但是,这个内置的URL重写支持不支持正则表达式,因而只能用来实现一对一的路径和页面的重写。urlMappings的配置内容包含在下面的Web.config文件中。

3、NBear.Web.Modules.UrlRewriteModule则是NBear中实现的一个基于HTTPModule的URL重写实现,它允许使用正则表达式来描述重写规则。

<?xml version="1.0"?>
<configuration>
  
  
<configSections>
    
<section name="UrlRewriteRules" type="NBear.Web.Modules.UrlRewriteRules, NBear.Web"/>
  
</configSections>
  
  
<UrlRewriteRules>
       
<Rule key="^/UrlRewriteSample/sample(.*).aspx" value="/UrlRewriteSample/Default.aspx?page=$1" />
       
<Rule key="^/UrlRewriteSample/section/(.*).aspx" value="/UrlRewriteSample/Default.aspx?section=$1" />
  
</UrlRewriteRules>

  
<system.web>

    
<urlMappings enabled="true">
      
<add url="~/buildin/Default.aspx" mappedUrl="~/Default.aspx?buildin=default" />
      
<add url="~/buildin.aspx" mappedUrl="~/Default.aspx?page=buildin" />
      
<add url="~/notexistfolder/buildin.aspx" mappedUrl="~/Default.aspx?page=buildin" />
    
</urlMappings>
    
    
<compilation debug="true">
      
<assemblies/>
    
</compilation>
    
    
<httpModules>
      
<add type="NBear.Web.Modules.UrlRewriteModule, NBear.Web" name="UrlRewriteModule"/>
    
</httpModules>
    
  
</system.web>
  
</configuration>

注意,代码中包含了urlMappings配置和用于NBear.Web.Modules.UrlRewriteModule重写规则。为了比较着几种重写方案,正则表达式基本上是和前面的IIRF定义中的规则类似的。

页面测试

定义完这些重写规则,我们就可以试着在页面中使用它们了。例如,如果我们写一个测试页面如下:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    
<title>Untitled Page</title>
</head>
<body>
    
<form id="form1" runat="server">
    
<div>
        
<href="buildin.aspx">buildin page</a>
        
<br />
        
<br />
        
<href="notexistfolder/buildin.aspx">buildin page</a>
        
<br />
        
<br />
        
<href="test1.aspx">test page</a>
        
<br />
        
<br />
        
<href="sample2.aspx">sample page</a>
        
<br />
        
<br />
        
<href="folder/1.aspx">folder page</a>
        
<br />
        
<br />
        
<href="section/2.aspx">section page</a>
        
<br />
        
<br />
        
<href="buildin">buildin default page</a>
        
<br />
        
<br />
        
<href="folder">folder default page</a>
        
<br />
        
<br />
        
<href="section">section default page</a>
        
<br />
        
<br />
        
<asp:Button ID="Button1" runat="server" Text="test postback" /></div>
    
</form>
</body>
</html>

Default.aspx.cs

public partial class _Default : System.Web.UI.Page 
{
    
protected void Page_Load(object sender, EventArgs e)
    
{
        Response.Write(Request.Path 
+ "?" + Request.ServerVariables["QUERY_STRING"]);
    }

}

注意,Default.aspx页面会输出当前呈现的实际页面及其QueryString参数。

运行该页面,分别点击页面中的链接,我们会看到,貌似所有的URL重写一切正常。但是,当试着点击页面中的按钮,我们马上会发现,页面postback后,浏览器地址栏中的链接变成了那个被重写后的地址,而不是,原来显示于地址栏的虚拟地址了。这是一个严重的不一致,没道理我点击页面的按钮,在没有跳转到其他页面的情况下,地址栏显示另一个页面地址,不是吗?

要解决这个问题,我们只需要为form添加一个onsubmit事件处理如下:

<form id="form1" runat="server" onsubmit="this.action=document.location.href">

添加该事件处理,就能在页面postback提交之前,重置页面的地址。

为前面的页面添加onsubmit之后,我们发现,postback不再会改变地址栏地址显示了。太棒了~~

。。。

先别沾沾自喜太多,你确认试过点击最后一组链接中的buildin default page和section default page了吗?

你会发现,这两个链接根本不能显示。为什么呢?为什么类似的folder default page可以正常显示,而另两个不能显示呢?

回到前面的规则定义部分,我们就能发现,folder default page使用的是由IIRF这个ISAPI定义的规则,而另两个则使用的是内置于ASP.NET2.0的HTTPModule的重写规则(本质上,urlMappings也是使用HTTPModule来实现重写的,所以,除了不支持正则表达式之外,它也包含自定义HTTPModule方式实现的所有缺点)。

在IIS的ISAPI层面,是可以截获所有的页面请求的,哪怕指定的页面、目录根本不存在。但是,ASP.NET解析器则只有在对页面的请求被IIS转发过来时,才能处理。我们知道,IIS可以忽略对链接的虚拟目录是否存在的检测,但是,却无法检测非ASP.NET支持的文件扩展名的链接(我们固然可以在IIS中将所有类型的扩展名都映射到ASP.NET解析器,但是,如果我们有设置IIS的权限,为什么还要用性能更低,限制更多的ASP.NET方式的URL重写,而不使用基于ISAPI方式的重写呢?)。所以,为了让这两个不能显示的页面能正常显示,一方面,我们要在IIS中设置默认页,如default.aspx,另一方面,需要让IIS对某个不带aspx扩展名的链接,如这里只包含某个目录的名称的链接转发到默认页。

要做到这一点,我们需要在我们的应用程序中,为buildin和section分别将两个对应的目录,并且,在目录中创建两个空的default.aspx页面。尽管这样的default.aspx页面实际上永远不会被真正执行,但是有他们的帮助,就能让IIS顺利地将页面请求转发至ASP.NET解析器,从而,使得基于HTTPModule的URL重写规则,被执行。

好了,创建这两个目录及default.aspx文件,我们就能修复该问题了。

。。。

别急着庆祝,还是多做点测试为好。:)

我们来对页面上的链接反复点击点击,folder page -> section page -> folder page -> section page...等等,打住,看到浏览器地址栏发生了什么吗?这不是恐怖活动,但是。。。也差不多了。我们看到我们可爱的地址,变成了...folder/section/folder/section...aspx。

想想是为什么呢?看看我们的aspx文件。。。我想你一定想到了。对了,都是相对路径惹的祸!

我们可爱的的相对路径一顶是同学们最常使用的,但是,浏览器在处理相对路径时,是以浏览器上接受的url地址为基础进行计算的,也就是说,如果当前的地址为folder/1.aspx,那么,很显然,./section/2.aspx这个页面,对应的自然是folder/section/2.aspx了,问题就出在这儿了!没有URL重写时,不会有这样的情况出现。但是URL重写,并且,将一个带假目录的虚拟地址重写到一个不带假目录的页面时,由于浏览器客户端和服务端此时的当前页面计算方法是不同的,就会发生相对路径的匹配错误问题!真实很严重的问题啊!

解决的办法,只有使用绝对路径!但是,我们当然不会傻到对每个链接直接使用绝对路径的,呵呵:)

将Default.aspx中的所有相对路径都使用Page.ResolveUrl进行包装如下,在输出页面时就将地址转换为绝对路径,就能解决这个恐怖的相对路径陷阱了。当然,也别忘了加上onsubmit事件处理代码:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    
<title>Untitled Page</title>
</head>
<body>
    
<form id="form1" runat="server" onsubmit="this.action=document.location.href">
    
<div>
        
<href="<%= ResolveUrl("buildin.aspx") %>">buildin page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("notexistfolder/buildin.aspx") %>">buildin page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("test1.aspx") %>">test page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("sample2.aspx") %>">sample page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("folder/1.aspx") %>">folder page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("section/2.aspx") %>">section page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("buildin") %>">buildin default page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("folder") %>">folder default page</a>
        
<br />
        
<br />
        
<href="<%= ResolveUrl("section") %>">section default page</a>
        
<br />
        
<br />
        
<asp:Button ID="Button1" runat="server" Text="test postback" /></div>
    
</form>
</body>
</html>

好了,再试一试点击页面上的链接和按钮,多点几次,再点,再点。。。

。。。

呼~

终于搞定了吧!^-^

posted @ 2006-09-11 13:31 Teddy's Knowledge Base Views(16522) Comments(31) Edit 收藏

 回复 引用 查看   
#1楼2006-09-11 13:57 | 阿不      
使用IIRF的性能似乎是最好的,但是它的缺点也很明显,与asp.net没有实际的关系,自定义能力可能没那么强。
asp.net 2.0内置的UrlMapping并不适用于多种场合下,在这种情况使用第三方HttpModule组件似乎是一种最好的选择,如果需要有更强的自定义能力,仍然可以自已实现,利用(HttpContext.RewritePath方法和正则表达式),具体可以参考CommunityServer的实现。
另外,不知道Teddy有没有注意到在asp.net 2.0中,使用RewritePath有一个BUG,见http://www.cnblogs.com/hjf1223/archive/2006/08/21/482356.html ,虽然标题是《由"~/"引发的搜索问题》,但后来发现,实际上是Asp.net 2.0进行Url重写时的一个BUG。

 回复 引用 查看   
#2楼[楼主]2006-09-11 14:02 | Teddy's Knowledge Base      
@阿不
~实际上只是在服务端被转换成了相对路径,注意还是相对路径!

所以,本质上使用它就和直接使用相对路径一样,避免不了我文中提到的相对路径陷阱问题。

 回复 引用   
#3楼2006-09-11 14:54 | C# hack[匿名][未注册用户]
还要支持正则表达式就完美了,HttpModule
 回复 引用 查看   
#4楼2006-09-11 14:58 | 阿不      
我不知道这个BUG是不是asp.net 2.0专有的,但是确实是我解决的方式是通过调用ResolveUrl方法来把"~/"解析为绝对路径就可以了,但是这样确实是有点太麻烦了。所以大家都认为它是个BUG。
 回复 引用 查看   
#5楼2006-09-11 15:03 | Jason Cui      
我觉得用正则来写,自定义能力已经足够强了吧,毕竟Apache也就是这个水平了。

将Default.aspx中的所有相对路径都使用Page.ResolveUrl进行包装
//没有更好的办法了吗?

 回复 引用 查看   
#6楼[楼主]2006-09-11 15:05 | Teddy's Knowledge Base      
@阿不
我不认为这是一个bug,就如我文中所说的,计算向对路径的过程是在客户端浏览器,由浏览器根据重写前的URL计算的,而重写前的URL我们无法预知其格式,因此,如果你的重写前的地址包含多个你所谓的级,那么,计算得来的如http://localhost/section1/section2/a.aspx页面中的一个到~/b.aspx页面的相对路径肯定就是../../b.aspx了的!!

 回复 引用 查看   
#7楼2006-09-11 15:13 | 阿不      
@Teddy's Knowledge Base
或许我们可以不认为它是个BUG,但是确实很奇怪的是,这个问题在普通浏览器中是不会出现的,而只有在 Search Enginer搜索站点的时候才会出现这个问题的。

 回复 引用 查看   
#8楼[楼主]2006-09-11 15:19 | Teddy's Knowledge Base      
@阿不
如果真像你说的“只有在 Search Enginer搜索站点的时候才会出现这个问题”的话,那也该是Search Enginer工具的bug,怎么能说是ASP.NET的bug呢?:)

 回复 引用 查看   
#9楼2006-09-11 15:33 | 阿不      
@Teddy's Knowledge Base
:( ,这样说吧。
同样Url Rewrite实现,同样的Search Enginer,在asp.net 1.1下不会有问题,但在asp.net 2.0会出错?那你能说是Search Enginer的bug?
我描述的不是很清楚,你可以看:
http://weblogs.asp.net/dfindley/archive/2006/09/05/Problems-with-RewritePath-and-Search-Engines_2E00_.aspx
http://todotnet.com/archive/2006/07/01/7472.aspx
(我还没有完全看懂呢。)

 回复 引用 查看   
#10楼[楼主]2006-09-11 15:47 | Teddy's Knowledge Base      
@阿不
看了一下你给的这个文章,他讲的问题的关键和URLRewrite没有本质关系。他说的是cookiless forms authentication的问题。ASP.NET2.0支持一种新的不依赖于cookie的session支持。默认情况下,ASP.NET自动判断客户端浏览器是否支持cookie,如果支持,则和asp.net1.1没什么区别。但是,如果浏览器不支持cookie,或者禁用cookie时,用户访问一个使用session的网站时,会有一个自动生成的sessionid被自动嵌到原来的url中,这就很可能会导致不支持cookie的Search Enginer得到错误的链接地址了。

但是就像原文所说的,如果显式的在web配置文件中指定网站必须使用cookie(默认设置是自动检测是否支持cookie),就能避免内迁进url的sessionid,就不会有问题了。

 回复 引用 查看   
#11楼2006-09-11 15:59 | 阿不      
@Teddy's Knowledge Base
谢谢你详细的解释,我也理解到与cookieless有关了,但是没有真正理解它的内在原因。但是我想这仍然可能会有大量的开发者没有注意到这个问题,所以在使用asp.net 2.0的Url Rewrite时,要特别注明。

 回复 引用 查看   
#12楼2006-09-11 16:16 | 发哥      
字体这么小,怎么看!
 回复 引用 查看   
#13楼[楼主]2006-09-11 16:20 | Teddy's Knowledge Base      
@发哥
字体不小的,大家都看得挺好。估计是你的浏览器的问题,你可以试试在页面上按住Ctrl键,同时滚动鼠标的滚轮,这样可以改变页面字体默认大小,很可能你以前不小心这样把浏览器默认字体改小了,你可以用该方法改回来。

 回复 引用   
#14楼2006-09-12 09:11 | 小鬼[未注册用户]
使用开源组件IIRF 可以从哪里下源代码?
 回复 引用 查看   
#15楼[楼主]2006-09-12 09:14 | Teddy's Knowledge Base      
@小鬼
IIRF的官方站点:
http://cheeso.members.winisp.net/IIRF.aspx

 回复 引用 查看   
#16楼2006-09-12 09:41 | mysoft      
学习
 回复 引用   
#17楼2006-09-13 21:05 | czyl[未注册用户]
<Rule key="^/UrlRewriteSample/sample(.*).aspx" value="/UrlRewriteSample/Default.aspx?page=$1" />
Teddy 的URL规则是通过正则表达式的分组然后再$1代替组一的吧?那不知是否支持多个分组,然后有组二($2)、组三($3)的形式呢?不过这种情况好像不怎么见到吧
另外有个问题:如果像写了几十个甚至上百个<Rule...>标签,而又要通过正则匹配的方式判断并映射,会不会对整个网站的效率影响很大?

 回复 引用 查看   
#18楼[楼主]2006-09-13 21:27 | Teddy's Knowledge Base      
@czyl
当然支持$2,$3。。。的,这里就是采用的标准的.Net类库的RegEx.Replace功能。
如果规则过多,肯定会影响性能的。所以使用url重写还是要时刻注意性能问题。

 回复 引用   
#19楼2006-09-28 17:07 | .net2.0[未注册用户]
这个能不能实现二级域名啊?
本人还在入门中呵。http://dotnet.blyct.com/QuickStartv20/default.aspx

 回复 引用   
#20楼2006-09-29 15:14 | effun[未注册用户]
MS的文档中对form的action处理是用另一种方法,代码如下:
public class ActionlessForm : System.Web.UI.HtmlControls.HtmlForm
{
/// <summary>
/// The RenderAttributes method adds the attributes to the rendered &lt;form&gt; tag.
/// We override this method so that the action attribute is not emitted.
/// </summary>
protected override void RenderAttributes(HtmlTextWriter writer)
{
// write the form's name
writer.WriteAttribute("name", this.Name);
base.Attributes.Remove("name");

// write the form's method
writer.WriteAttribute("method", this.Method);
base.Attributes.Remove("method");

// remove the action attribute
base.Attributes.Remove("action");

// finally write all other attributes
this.Attributes.Render(writer);

if (base.ID != null)
writer.WriteAttribute("id", base.ClientID);
}

}

它的原理很简单,就是重写基类的RenderAttributes(),清空form的action属性。

 回复 引用 查看   
#21楼2006-10-06 14:40 | mactea      
这是作者Sample的原例:

在 ASP.NET 中执行 URL 重写
http://www.microsoft.com/china/msdn/library/webservices/asp.net/URLRewriting.mspx

 回复 引用   
#22楼2006-10-23 19:16 | ggyy200202011[未注册用户]
使用相对应用程序根目录就行了。而不是相对路径,绝对路径,在重写前都要转换成相对应用程序根目录来进行的。
 回复 引用 查看   
#23楼2006-10-23 20:30 | 飞行石      
Teddy同学的这个方案有两小点我觉得不妥:为了让UrlRewriting的Mapping方案具有可读性,应该适当在表示方式上尽量兼容大家都习惯的或是标准的名称,如:


<UrlMappings>
    
<add url="community/(\d+).html" mappedUrl="~/template.aspx?action=community.default&amp;cid=$1"/>
    
<add url="group/(\d+)/create.html" mappedUrl="~/template.aspx?action=group.create&amp;cid=$1"/>
</UrlMappings>


 

当然,这只是建议。

 

Form 的Action属性可以通过服务器端继承一个HtmlTextWriter来写。
这样不必在客户端写onsubmit来实现,因为在一定程度上已经违反了URL重写的我认为是原则的原则:不能暴露真实URL,显然这是违反了的。

 

HtmlTextWriter的用法我是参考Cuyahoga的原理来完成的,这里需要两个步骤:


  1. 继承一个HtmlTextWriter对象,设计一个FormFixer对象来完成对URL重写后Form的Action属性的纠正。这个FormFixer对象是给BasePage专用的,所以可以声明为内部类;
  2. 继承一个Page对象,设计自己的BasePage,然后该项目所有继承自Page的类或aspx文件都直接或间接继承自这个BasePage;


为了方便而把这两个类写在一起了,其中FormFixer对象直接作为BasePage的内部类,关键的方法和代码部分我用粗体表示:



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Babatang.Web
{
    
public class BasePage : Page
    {
        
protected override void Render(HtmlTextWriter writer)
        {
            writer 
= new FormFixer(writer);
            
base
.Render(writer);
        }
    }

    
internal class FormFixer : HtmlTextWriter
    {
        
private bool _isInForm = false;

        
public FormFixer(System.IO.TextWriter writer) : base(writer) { }

        
public FormFixer(System.IO.TextWriter writer, string tagString) : base(writer, tagString) { }

        
public override void WriteBeginTag(string tagName)
        {
            
if (string.Compare(tagName, "form"!= 0)
                
this._isInForm = true;
            
base.WriteBeginTag(tagName);
        }

        
public override void WriteAttribute(string name, string value, bool fEncode)
        {
            
if (this._isInForm && name.Equals("action"))
                value 
= HttpContext.Current.Request.RawUrl;
            
base.WriteAttribute(name, value, fEncode);
        }
    }
}




另外贴个小技巧:


  1. 如果是在VS2003 .NET 1.1 下我暂时没发现什么,查找替换的方法足矣;
  2. 如果是在ASP.NET 2.0 下并且没有使用后台文件的话可以在web.config文件中加上一段配置就可以实现,其中关键部分用粗体表示:
    <pages enableViewState="false"
        validateRequest
    ="false"
        maintainScrollPositionOnPostBack
    ="true"
        pageBaseType
    ="Babatang.Web.BasePage,Babatang.Web"
        userControlBaseType
    ="Babatang.Web.BaseControl,Babatang.Web"
        theme
    ="babatang">

    该站点下所有单页文件都默认继承这个BasePage了。当然,如上面所写的userControlBaseType属性一样,UserControl也可以象Page这么干。
  3. 如果是 .NET 2.0 下并且使用了后台文件的话我还不知道有什么好办法,因为即使如第二条那样在web.config中声明了页面基类的话在aspx.cs文件中仍然是继承那个默认的Sytem.Web.UI.Page这个对象了,仍然只有查找替换的办法。


这样,我的一个虚拟URL:http://www.babatang.com/community/mjxc/default.aspx 实际上就是几乎真实的URL了。即使看源代码也没有其真实URL的影子,对于访客来说这就是URL。

 

这是俺在03-04年翻译的一篇文章,http://www.cnblogs.com/xspin/articles/30788.aspx,比MSDN翻译的早了许多,没事儿看看吧,Scott的这篇原文让我学了很多东西,推荐看看。

Blog一直都不习惯,就当是论坛回帖吧,有问题也请继续回帖。
可惜这里不能顶

 回复 引用 查看   
#24楼2006-10-23 20:57 | 飞行石      
另外URL Rewriting前后的参数保留问题有时候也要考虑,比如在页面上做一个分页,又只想把页码做为参数而不参与URL重写的话(http://www.babatang.com/community/mjxc/default.aspx?offset=2),同时如果正好要重写出一个参数和虚拟URL上的参数重合的话还要考虑是否覆盖的问题。

 回复 引用   
#25楼2007-01-18 15:13 | Geni[未注册用户]
在Web.Config文件中
<UrlRewriteRules>
<Rule key="^/UrlRewriteSample/sample(.*).aspx" value="/UrlRewriteSample/Default.aspx?page=$1&amp;id=$2" />
</UrlRewriteRules>
我用 Request.QueryString["ID"].ToString() 取不到id的值。
在页面中怎么得到'id'的值?

 回复 引用   
#26楼2007-01-21 13:41 | www.8o8o8.com[未注册用户]
etoo games


 回复 引用   
#27楼2007-02-02 00:36 | melorain[未注册用户]
很好用啊。
关于路径,我觉得并非一定要用绝对的,比如
<Rule key="/show/([a-z0-9]*).aspx" value="/A.aspx?cid=$1" />
// 若页面中链接为相对的alter/(*).aspx,那么再加一个处理它的:
<Rule key="/show/alter/([a-z0-9]*).aspx" value="/B.aspx?cid=$1" />
也就是说,将错就错,URL往长里弄

 回复 引用 查看   
#28楼2007-11-22 09:22 | ainy360      
谢谢各位大大,学习中
 回复 引用   
#29楼2009-09-22 20:04 | 好好笑[未注册用户]
你们都说得很好。呵呵,小弟学习了。
 回复 引用 查看   
#30楼2009-11-16 13:00 | jameshe      
飞行石重写Render的方法很值得推荐