Hello World
Spiga

重提URL Rewrite(4):不同级别URL Rewrite的一些细节与特点

2008-01-13 16:35 by 老赵, 17927 visits

在之前的文章里我们已经谈论了有关URL Rewrite的几个主要的方面。在本系列的最后一篇文章中,我们就来讨论一下有关不同级别URL Rewrite的一些细节与特点。

理论上说,IIS级别的URL Rewrite使用C或C++编写,比使用托管代码编写的ASP.NET级别URL Rewrite性能要高。但是我认为这方面的差距在大部分情况下可以忽略不计,这种性能几乎不可能成为性能瓶颈。因此选择何种级别的URL Rewrite一般不会由您应用程序的性能要求来决定。那么到底应该使用哪种级别的URL Rewrite呢?在使用不同级别的URL Rewrite之后,我们又该注意点什么呢?我在这里谈谈我个人的看法。

对URL Rewrite功能上的要求

虽说目前的URL Rewrite组件在功能上已经能够满足大部分的应用,但是在某些时候,我们的确还是会需要一些特殊的功能。例如根据域名进行URL Rewrite,就目前的URL Rewrite组件来说,想要实现这个并不容易。商业化的ISAPI Rewrite目前已经可以支持这一点,可惜开源的UrlRewriter.NET和IIRF在这方面功能都有所不足。它们都是根据请求相对于该站点的路径来匹配,至于请求的是哪个域名并不能作为匹配条件来使用。这就要求我们对URL Rewrite组件进行扩展。对于大部分.NET开发人员来说,托管代码自然是开发首选,这时可能就要选择ASP.NET级别的URL Rewrite重写组件了。不过目前网上能找到不少扩展的例子,无论是ASP.NET级别的UrlRewriter.NET还是IIS级别的IIRF。

不过事实上,如果要实现上述功能,我们也可以分两步进行。首先我们在IIS级别使用IIRF进行URL Rewrite,接着在ASP.NET级别作进一步的URL Rewrite。例如我们现在要实现将“http://jeffz.domain.com/articles”重写为“/ArticleList.aspx?owner=jeffz”,就可以先在让IIRF做第一次URL Rewrite,目的是将“/articles”重写至“/ArticleList.aspx”。

RewriteRule    ^/Articles$    /ArticleList.aspx      [I, L, U] 

这样,ASP.NET引擎就会直接接收到一个针对/ArticleList.aspx的请求了。然后在ASP.NET内部,我们可以作第二次的URL Rewrite(方便起见,我这里还是在Global.asax里写,在项目中还是建议使用额外的HttpModule来实现)。

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext context = HttpContext.Current;
 
    string host = context.Request.Url.Host;
    string owner = host.Substring(0, host.IndexOf('.'));
 
    context.RewritePath(context.Request.RawUrl + "?owner=" + owner);
}

经过两次URL Rewrite,已经实现了我们想要的效果(在实际项目中,上面的代码不能直接使用,因为需要判断是否有Query String等等)。

此外,ASP.NET级别的URL Rewrite只能在ASP.NET里工作(显然的事情),如果要让URL Rewrite支持PHP,RoR等其他服务器技术,就只能使用IIS级别的URL Rewrite了(或者其他服务器技术提供的URL Rewrite功能)。

对URL中特殊字符的处理

有些特殊字符是不允许出现在URL中的,或者一旦出现在URL里以后,请求的含义就被改变了。例如我们需要对搜索页面进行URL Rewrite,将“/Search/xxx”重写为“/Search.aspx?xxx”,然后可以根据问号后面的字符串获得用户提供的关键字。如果使用UrlRewriter.NET,我们就会使用如下的配置:

<rewriter>
  <rewrite url="^/Search/(.+)$" to="~/Search.aspx?$1" processing="stop" />
</rewriter>

普通情况下,这个URL Rewrite工作正常。但是如果用户使用“%” 作为关键字,情况就不一样了,因为我们会收到如下的错误页面提示:

Bad Request

这是因为URL中是不允许出现“%”的。大家可以去各种网站上尝试着请求一些例如“ABC%25DEF”的路径(“%25”之后即为“%”),大都能发现“400 Bad Request”错误。不过将“%”放在Query String里倒是合法的——对阿,我们不是将keyword重写到Query String里了吗?为什么还是不行呢?这还是由于ASP.NET执行方式决定的。

IIS ASP.NET

Bad Request是在上图的步骤3,也就是还在进行初始化的时候就被确定了。而我们的URL Rewrite是在第4步BeginRequest事件中才发生的。当请求中带有非法字符时,我们根本还没有机会进行URL Rewrite。

那么我们怎么处理这个问题呢?在一般情况下,我们在客户端将%去除也不会有太大问题(有些站点的确是这么做的),但是如果非要保留呢?那么就使用Query String来传递参数吧,或者我们也可以使用IIS级别的URL Rewrite。还是以IIRF为例:

RewriteRule    ^/Search/(.+)$    /Search.aspx?$1      [I, L, U] 

  当请求被发送到IIS之后(步骤一),并且在选择应该交给哪个ISAPI执行(步骤二)之前就发生了URL Rewrite。经过了URL Rewrite之后的地址,其中的“%”已经被转移到了Query String中,这时候交由ASP.NET处理时自然已经合法了。

出错页面配置

最后我们来讨论出错页面的配置。例如,一般来说我们都会为应用配置一个404错误页面,这样用户在访问一个不存在的资源时我们可以给他查看一个特定的页面,而不是默认的错误提示。但是在这一点上,不同级别的URL Rewrite就要使用不同的方法进行配置。

如果我们使用了ASP.NET级别的URL Rewrite,一般来说我们已经在IIS里设置了Wildcard Mapping,这样任意的请求(包括html,jpg等)都会交由ASP.NET处理。如果请求了一个不存在的资源,404错误将由ASP.NET发出,因此404错误页面应该在web.config中进行配置:

<customErrors mode="On" defaultRedirect="GenericErrorPage.htm">
  <error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>

如果我们使用了IIS级别的Url Rewrite,我们不会配置Wildcard Mapping。也就是说我们只有在Rewrite之后的地址为aspx(或其他原本就该交由ASP.NET ISAPI处理)的情况下,ASP.NET引擎才会开始工作。如果用户请求了一个不存在的资源,那么404错误将由IIS发出,这时候404错误页面应该在IIS里进行配置:

Custom Error in IIS

 

至此,有关URL Rewrite的话题已经讨论完了。在实际开发中肯定还会遇到各种各样不同的情况,但是只要理解了URL Rewrite方式的关键,按照程序运行的方式来思考,相信一般情况下不太会遇到难以处理的问题。

相关链接:

(1)IIS与ASP.NET

(2)使用已有组件进行URL Rewrite

(3)在URL Rewrite后保持PostBack地址

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

50 条回复

  1. andy.wu
    *.*.*.*
    链接

    andy.wu 2008-01-13 17:19:00

    老赵的文章写得很扎实。

  2. Awen
    *.*.*.*
    链接

    Awen 2008-01-13 17:32:00

    类似博客园这样的URLuserName.cnblogs.com的实现,不知道老赵有没有了解过

  3. 老赵
    admin
    链接

    老赵 2008-01-13 17:34:00

    @Awen
    我在这篇文章里不是已经写了吗?

  4. 张旋转[未注册用户]
    *.*.*.*
    链接

    张旋转[未注册用户] 2008-01-13 17:41:00

    文章写的很好

  5. sunny1[未注册用户]
    *.*.*.*
    链接

    sunny1[未注册用户] 2008-01-13 18:16:00

    老赵,我也想做一个社会性的网站,请问一下,用什么ajax框架比较好(Jquery和moontools)哪个更好些,Jquery功能各方面很强大,在操作Dom上好像存在性能问题吗?能帮我推荐一个比较好的吗?

  6. 老赵
    admin
    链接

    老赵 2008-01-13 19:43:00

    @sunny1
    我现在用mootools,要我推荐还真推荐不出来。应该都可以吧。

  7. test1[未注册用户]
    *.*.*.*
    链接

    test1[未注册用户] 2008-01-13 21:23:00

    老赵能不能做个demo?更具体些!

  8. stonezhu
    *.*.*.*
    链接

    stonezhu 2008-01-13 21:39:00

    发现左边我的衣橱,是老赵的创业项目?
    网站做的挺精致的

  9. 老赵
    admin
    链接

    老赵 2008-01-13 22:19:00

    @test1
    哪里还需要例子呢?

  10. 风景年华
    *.*.*.*
    链接

    风景年华 2008-01-13 22:35:00

    好文章,越来越有味道了,呵呵,支持一个!

  11. 东[未注册用户]
    *.*.*.*
    链接

    东[未注册用户] 2008-01-13 23:34:00

    每次看老赵的文章都有收获,多谢分离...........

  12. 东[未注册用户]
    *.*.*.*
    链接

    东[未注册用户] 2008-01-13 23:35:00

    分享...:)

  13. lygwujian[未注册用户]
    *.*.*.*
    链接

    lygwujian[未注册用户] 2008-01-14 08:32:00

    想问一下你的网站是加在哪一家的服务器上,一年多少钱啊?

  14. 小菲[未注册用户]
    *.*.*.*
    链接

    小菲[未注册用户] 2008-01-14 08:37:00

    希望赵大虾能把UrlRewrite做成一个完整的产品,象isapi_rewrite;我觉得你完全有实力做到,为什么不呢?

  15. Awen
    *.*.*.*
    链接

    Awen 2008-01-14 09:29:00

    @Jeffrey Zhao
    恩,思路清晰了,呵呵!

  16. 老赵
    admin
    链接

    老赵 2008-01-14 10:42:00

    @小菲
    嗯……再议吧,可能会给个扩展啥的,呵呵……

  17. jasduke[未注册用户]
    *.*.*.*
    链接

    jasduke[未注册用户] 2008-01-14 14:10:00

    相当有内容的一个系列,赞一个。

  18. bubble[未注册用户]
    *.*.*.*
    链接

    bubble[未注册用户] 2008-01-14 21:16:00

    好像这个人在 IIRF 基础上 实现了 二级域名重写的功能

    company.com/usersite.asp?sitename=abc
    重写到
    abc.company.com

    链接地址:
    http://zhangsichu.com/blogview.asp?Content_Id=82

  19. (武眉博<活靶子Net>  )[未注册用户…
    *.*.*.*
    链接

    (武眉博<活靶子Net> )[未注册用户] 2008-01-15 08:58:00

    我写过一个空间做多个站,各个域名分别不同目录的重写
    http://www.cnblogs.com/huobazi/archive/2005/10/15/SubdomainsWithHttpModuleInAspDotNet.html
    二级域名类似

  20. 老赵
    admin
    链接

    老赵 2008-01-15 11:01:00

    @(武眉博<活靶子Net> )
    其实要做总是容易的,呵呵。只是没有现成的开源组件。

  21. Cat Chen
    *.*.*.*
    链接

    Cat Chen 2008-01-16 16:21:00

    URL中不允许在路径使用%是RFC规定了的吗?如果是规定了的,你在IIS进行rewrite也没有用啊,因为proxy可能直接对不符合规定的地址返回400。

  22. 老赵
    admin
    链接

    老赵 2008-01-16 18:10:00

    @Cat Chen
    嗯,Proxy,的确可能。

  23. 一抹微蓝
    *.*.*.*
    链接

    一抹微蓝 2008-01-17 17:30:00

    UrlRewritingNet好像是可以对域名进行重写的

  24. conannb[未注册用户]
    *.*.*.*
    链接

    conannb[未注册用户] 2008-01-21 17:20:00

    写的很好,学习下

  25. Argo
    *.*.*.*
    链接

    Argo 2008-01-22 14:21:00

    提一个问题:
    如果我想用URLRewriter功能,我的网页中的URL应该呈现的是用户友好连接,我应该如何做呢?(这个工程好像正好和URLRewriter的工作相反,这个连接用户点击后,URLRewriter重新把它改回我们程序需要处理的地址)我们开发的时候有时候需要在页面上加入一些静态的URL,如果我们写成用户友好的URL后导致开发环境有些麻烦,比如必须配置URLRewriter。

    能不能做到如下要求:开发人员不管URLRewriter,把这个功能丢给部署,在部署的时候来做这件事?

  26. 老赵
    admin
    链接

    老赵 2008-01-22 14:42:00

    @Argo
    你的意思是说,再源代码里写“xxx.aspx?id=3”,然后在页面里生成“xxxx/3”吗?这个可能比较难以做到。

  27. Argo
    *.*.*.*
    链接

    Argo 2008-01-22 15:48:00

    是啊。但是我们应用了URLRewriter,我们自然希望我们的网页中Render的网址链接最好都是"xxxx/3"样式的,但是这样做对我们的开发环境不是很好,尤其是比较大的Team。如果都是服务器端的HyperLink控件还好弄。但是有些就是<a>的。还有一些是其他的,不知道如何做才能做到完美。

  28. 老赵
    admin
    链接

    老赵 2008-01-23 03:25:00

  29. Cat Chen
    *.*.*.*
    链接

    Cat Chen 2008-01-23 19:01:00

    @Jeffrey Zhao
    所以我一般不建议利用服务器端和客户端的兼容性来容忍一些不符合rfc的URL,因为proxy随时可能以400 bad request把这个URL拦截掉。

  30. 老赵
    admin
    链接

    老赵 2008-01-23 19:17:00

    @Cat Chen
    有道理的

  31. 卡卡 ^ cacard
    *.*.*.*
    链接

    卡卡 ^ cacard 2008-01-27 22:00:00

    问一句。
    MSDN出的那个URLRewriter.dll在ASP.net1.1和2.0中都可以使用吧~~

  32. 老赵
    admin
    链接

    老赵 2008-01-27 22:58:00

    @卡卡 ^ cacard
    1.1能用的2.0肯定可以用,如果不能用,重新编译就好了。

  33. 侯垒
    *.*.*.*
    链接

    侯垒 2008-01-31 08:51:00

    学习了.

  34. dusonchen[未注册用户]
    *.*.*.*
    链接

    dusonchen[未注册用户] 2008-02-06 17:44:00

    urlrewrite还有一个重要的作用-任意截断(即/archive/2008/01/可以看到1月份的文章,而/archive/2008/可以看到2008年的全部文章),不是吗?这方面好像没有提及哦

  35. 老赵
    admin
    链接

    老赵 2008-02-06 19:15:00

    @dusonchen
    你说的是URL Rewrite在业务上的运用,而不涉及URL Rewrite技术本身,不在文章涉及范围内,呵呵。

  36. 您好[未注册用户]
    *.*.*.*
    链接

    您好[未注册用户] 2008-03-21 17:46:00

    您好,问您个问题,我使用的是Intelligencia.UrlRewriter;
    我想实现以下URL重写:
    default.aspx?x=1&y=2用default.html?x=1&y=2

    <rewrite url="~/default.html" to="~/default.aspx?x=$1&y=$2" processing="stop" />

    rewrite中的url的正则怎么写?



  37. 老赵
    admin
    链接

    老赵 2008-03-21 20:44:00

    @您好
    不应该这么写,应该把~/default.html(.*)重写到~/default.aspx$1。

  38. 感動常在
    *.*.*.*
    链接

    感動常在 2008-03-31 17:20:00

    這是我第二遍看

  39. 左派[未注册用户]
    *.*.*.*
    链接

    左派[未注册用户] 2008-04-01 15:40:00

    没太弄明白老赵的意思,你是说iirf不能实现二级域名,需要在.net的部分去实现吗?

    给兄弟们一个我现在用的方法.

    RewriteCond %{SERVER_NAME} ^(\b(?!www|bbs|ftp)\w{4,})\.xxxx\.net$ [I]
    RewriteRule ^/$ http://my.xxxx.net/user.aspx?id=%1 [R]

    这样就可以了.用SERVER_NAME这个变量.


  40. MOki[未注册用户]
    *.*.*.*
    链接

    MOki[未注册用户] 2008-08-25 22:53:00

    使用UrlRewriter.net以后,css和图片问题怎么解决?css和图片等出错了。。。。。。。。

  41. 老赵
    admin
    链接

    老赵 2008-08-27 09:32:00

    @MOki
    把CSS的Rewrite规则设高一些。

  42. 黑虫[未注册用户]
    *.*.*.*
    链接

    黑虫[未注册用户] 2008-10-09 20:30:00

    为什么我配了虚拟目录后 重写错误 报的是404
    但是我在项目里点在浏览器查看是对的
    请问错在哪呢?

  43. 老赵
    admin
    链接

    老赵 2008-10-09 22:48:00

    @黑虫
    IIS里配置Wildcard Mapping

  44. Patriot[未注册用户]
    *.*.*.*
    链接

    Patriot[未注册用户] 2008-11-03 14:20:00

  45. China wholesale[未注册用户…
    *.*.*.*
    链接

    China wholesale[未注册用户] 2009-02-23 17:45:00

    好文章啊!顶一下

  46. zahota[未注册用户]
    *.*.*.*
    链接

    zahota[未注册用户] 2009-02-27 03:56:00

    如果网站使用了Url重写,那么在web.config文件中的customErrors配置节设置defaultRedirect属性后
    当出现错误时都会跳转至defaultRedirect指定的页面。但是会出现一下情况
    比如将list.aspx?id=1 重写为liat/1.aspx的话。那么当在list/1.aspx里发生异常,将跳转至defaultRedirect指定的页面。这里假设defaultRedirect属性设置为error.aspx
    那么发生异常时error.aspx的url为error.aspx?aspxerrorpath=/list.aspx这样的形式。
    我希望aspxerrorpath参数为重写后的地址,比如error.aspx?aspxerrorpath=/list/1.aspx这样的形式。

  47. 老赵
    admin
    链接

    老赵 2009-02-27 09:07:00

    @zahota
    自己handle吧。

  48. chaoliu
    *.*.*.*
    链接

    chaoliu 2009-03-07 13:39:00

    谢谢了.这个cc318.com就是我才改的,哈哈

  49. 链接

    奎彤 2010-11-23 19:38:32

    我的问题是有没有什么方法可以自动化URL Rewrite.

    是否一定要用[人工方式]帮每一个*.ASPX及配对的QueryString撰写一段RewriteRule。

    例如:

    • 1/2/a,重写为a.apsx?aa=1&bb=2
    • 1/2/3/b,重写为b.aspx?cc=1&dd=2&ee=3

    在企业应用的环境下,假如是一个多人编程的Team,*.ASPX及QueryString是持续变化及增长的状况。

    若要用人工方式维护,可能会遭遇困难:

    1. 即使在开发前约定好URL规则,没有办法约束程序员一定按照规则写URL,还要花成本检查源码。
    2. 要程序员在写原本要实现的逻辑之外,还需要另外注意URL的写法并生成一套RewriteRule,增加了开发的负担及复杂度。
    3. 要不断整合新的RewriteRule到ini檔,同样增加了复杂度。

    那是否什么方法可以自动化生成URL Rewrite?

  50. 葫芦
    222.88.61.*
    链接

    葫芦 2011-07-16 10:33:13

    支持姐夫。 扎实,看一篇文章就像吃一碗家乡的烩面。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我