ASP.net的地址重写(URLRewriter)实现原理及代码示例

吴剑 2007-01-01

原创文章,转载必需注明出处:http://www.cnblogs.com/wu-jian/

概述

访问者输入:http://wu-jian.cnbolgs.com/default.aspx,实际请求和响应的地址却是:http://www.cnblogs.com/wu-jian/default.aspx, 这就是UrlRewrite,除了实现二级域名功能,它在简化用户输入地址、SEO、网站版本迭代更新等多个方面发挥着重要作用。

微软曾在.net framework 1.1中提供过一个名为URLRewriter的小工具供开发人员轻松实现UrlRewrite,下载地址为:http://download.microsoft.com/download/0/4/6/0463611e-a3f9-490d-a08c-877a83b797cf/MSDNURLRewriting.msi

本文以URLRewriter为例,在.net framework 2.0的环境下做了小部分优化调整,供大家学习和参考,能力有限,不足之处请大家及时指出。本文假设读者对URLRewriter、ASP.net的 Http管线有一定了解,否则请查阅相关资料。

配置

URLRewriter在web.config里通过自定义配置结合正则表达式来实现URL重写。

自定义节点的声明:

  1. <configSections>
  2. <section name="RewriterConfig"
  3. type="PaoTiao.PTRewriter.Config.RewriterConfigSerializerSectionHandler, PaoTiao.PTRewriter"/>
  4. </configSections>

自定义节点配置项:

  1.  
  2. <RewriterConfig>
  3. <Rules>
  4. <RewriterRule>
  5. <LookFor>^http://([a-zA-Z0-9]{4,16}).cnblogs.com/default.aspx$</LookFor>
  6. <SendTo>/$1/default.aspx</SendTo>
  7. </RewriterRule>
  8.  
  9. <RewriterRule>
  10. <LookFor>^http://www.cnblogs.com/([a-zA-Z0-9]{4,16})/$</LookFor>
  11. <SendTo>/test/url.aspx?p=$1</SendTo>
  12. </RewriterRule>
  13. </Rules>
  14. </RewriterConfig>

如上我配置了两个规则,以实例说明,第一个可将:http://wu-jian.cnblogs.com 重写到:/wu-jian/default.aspx

第二个可将:http://www.cnblogs.com/wu-jian 重写到:/test/url.aspx?p=wu-jian

但微软的URLRewriter LookFor并不支持到域名位置,它只能在根目录之后做文章,截选了它源码DEMO中的一段:

  1. <RewriterRule>
  2. <LookFor>~/(\d{4})/(\d{2})/(\d{2})\.aspx</LookFor>
  3. <SendTo>~/ShowBlogContent.aspx?year=$1&amp;month=$2&amp;day=$3</SendTo>
  4. </RewriterRule>

可以发现,当需要使用二级域名或自定义级别更高的rewrite时,URLRewriter是不支持的,所以在此我将源代码作了一小部分优化,匹配与重写都使用LookFor和SendTo中的原始表达式,不做任何智能替换与修改。其实很多时候,在微软的产品中都能发现这种“画蛇添足”的影子。

关于匹配与替换, 其实就是应用了正则表达式中的“反向引用”原理,在我的博客里有代码示例,不熟悉正则的朋友可去了解,此处不作详叙。

源代码分析

对自定义配置进行访问的类:

  1.  
  2. using System;
  3. using System.Configuration;
  4. using System.Xml;
  5. using System.Xml.Serialization;
  6. using System.Xml.XPath;
  7.  
  8. namespace PaoTiao.PTRewriter.Config
  9. {
  10. ///<summary>
  11. /// 实现IConfigurationSectionHandler接口,以对自定义节点进行访问
  12. ///</summary>
  13. publicclass RewriterConfigSerializerSectionHandler : IConfigurationSectionHandler
  14. {
  15. ///<summary>
  16. /// 该方法无需主动调用
  17. /// 它在ConfigurationManager.GetSection()被调用时根据改配置节声明中所定义的类名和路径自动实例化配置节处理类
  18. ///</summary>
  19. publicobject Create(object parent, object configContext, System.Xml.XmlNode section)
  20. {
  21. XmlSerializer ser =new XmlSerializer(typeof(RewriterConfiguration));
  22. return ser.Deserialize(new XmlNodeReader(section));
  23. }
  24.  
  25. }//end class
  26. }

之前一直写WEB程序,很少用到自定义节点,直到一次写Windows Service用到了app.config,发现要读取自定义节点,就需要实现IConfigurationSectionHandler接口。

  1.  
  2. using System;
  3. using System.Web;
  4. using System.Web.Caching;
  5. using System.Configuration;
  6. using System.Xml.Serialization;
  7.  
  8. namespace PaoTiao.PTRewriter.Config
  9. {
  10. [Serializable()]
  11. [XmlRoot("RewriterConfig")]
  12. publicclass RewriterConfiguration
  13. {
  14. private RewriterRuleCollection rules;
  15.  
  16. ///<summary>
  17. /// 该方法从web.config中读取规则集合,并使用了Cache以避免频繁IO操作
  18. ///</summary>
  19. ///<returns></returns>
  20. publicstatic RewriterConfiguration GetConfig()
  21. {
  22. //使用缓存
  23. if (HttpContext.Current.Cache["RewriterConfig"] ==null)
  24. HttpContext.Current.Cache.Insert("RewriterConfig", ConfigurationManager.GetSection("RewriterConfig"));
  25.  
  26. return (RewriterConfiguration)HttpContext.Current.Cache["RewriterConfig"];
  27. }
  28.  
  29. public RewriterRuleCollection Rules
  30. {
  31. get { return rules; }
  32. set { rules = value; }
  33. }
  34.  
  35. }//end class
  36. }  

我想使用UrlRewrite的站点绝大部分都是面向公众用户的,面向公众用户就面临着大的流量和并发,谁也不愿意为每个请求去读取一次web.config吧,那么在此处使用Cache是明智之举。另外更换了已过期的ConfigurationSettings.GetConfig()方法为ConfigurationManager.GetSection()方法。

如下两个类完成类似的Model功能。

  1.  
  2. using System;
  3. using System.Collections;
  4.  
  5. namespace PaoTiao.PTRewriter.Config
  6. {
  7. ///<summary>
  8. /// 规则集合
  9. ///</summary>
  10. [Serializable()]
  11. publicclass RewriterRuleCollection : CollectionBase
  12. {
  13. ///<summary>
  14. /// 向集合中添加新规则
  15. ///</summary>
  16. ///<param name="r">RewriterRule对象</param>
  17. publicvirtualvoid Add(RewriterRule r)
  18. {
  19. this.InnerList.Add(r);
  20. }
  21.  
  22. ///<summary>
  23. /// 获取或设置项
  24. ///</summary>
  25. public RewriterRule this[int index]
  26. {
  27. get { return (RewriterRule)this.InnerList[index]; }
  28. set { this.InnerList[index] = value; }
  29. }
  30.  
  31. }//end class
  32. }  
  1.  
  2. using System;
  3.  
  4. namespace PaoTiao.PTRewriter.Config
  5. {
  6. ///<summary>
  7. /// 重写规则的数据对象
  8. ///</summary>
  9. [Serializable()]
  10. publicclass RewriterRule
  11. {
  12. privatestring mLookFor;
  13. privatestring mSendTo;
  14.  
  15. ///<summary>
  16. /// 查找规则
  17. ///</summary>
  18. publicstring LookFor{
  19. get { returnthis.mLookFor; }
  20. set { this.mLookFor = value; }
  21. }
  22.  
  23. ///<summary>
  24. /// 重写规则
  25. ///</summary>
  26. publicstring SendTo{
  27. get { returnthis.mSendTo; }
  28. set { this.mSendTo = value; }
  29. }
  30.  
  31. }//end class
  32. }//end namespace

使用HttpModule实现地址重写

  1.  
  2. using System;
  3. using System.Web;
  4.  
  5. namespace PaoTiao.PTRewriter
  6. {
  7. ///<summary>
  8. /// 实现IHttpModule的抽象类
  9. ///</summary>
  10. publicabstractclass BaseModuleRewriter : IHttpModule
  11. {
  12. publicvirtualvoid Dispose() { }
  13.  
  14. publicvirtualvoid Init(HttpApplication app)
  15. {
  16. app.BeginRequest +=new EventHandler(this.BaseModuleRewriter_BeginRequest);
  17. }
  18.  
  19. protectedvirtualvoid BaseModuleRewriter_BeginRequest(object sender, EventArgs e)
  20. {
  21. HttpApplication app = (HttpApplication)sender;
  22. Rewrite(app);
  23. }
  24.  
  25. ///<summary>
  26. /// 地址重写抽象函数
  27. ///</summary>
  28. ///<param name="app"></param>
  29. protectedabstractvoid Rewrite(HttpApplication app);
  30.  
  31. }//end class
  32. }

在Http模块中进行核心逻辑处理,源代码是在AuthorizeRequest事件中,此处我使用了BeginRequest事件。

对抽象方法Rewrite的实现。大家可以发现URL重写其实就一个核心方法:HttpContext.RewritePath

看看MSDN中对该方法的描述:指定内部重写路径,并允许请求的 URL 与资源的内部路径不同。

  1.  
  2. using System;
  3. using System.Text.RegularExpressions;
  4. using System.Configuration;
  5.  
  6. using System.IO;
  7.  
  8. namespace PaoTiao.PTRewriter
  9. {
  10. publicclass ModuleRewriter : BaseModuleRewriter
  11. {
  12. ///<summary>
  13. /// 地址重写函数
  14. ///</summary>
  15. ///<param name="app"></param>
  16. protectedoverridevoid Rewrite(System.Web.HttpApplication app)
  17. {
  18. //开始跟踪日志
  19. app.Context.Trace.Write("ModuleRewriter", "Entering ModuleRewriter");
  20.  
  21. //获取规则集合
  22. Config.RewriterRuleCollection rules = Config.RewriterConfiguration.GetConfig().Rules;
  23.  
  24. for (int i =; i < rules.Count; i++)
  25. {
  26. string lookFor = rules[i].LookFor;
  27. Regex reg =new Regex(lookFor, RegexOptions.IgnoreCase);
  28.  
  29. if (reg.IsMatch(app.Request.Url.ToString()))
  30. {
  31. //获取目的URL
  32. string sendToUrl = reg.Replace(app.Request.Url.ToString(), rules[i].SendTo);
  33.  
  34. //跟踪日志
  35. app.Context.Trace.Write("ModuleRewriter", "Rewriting URL to "+ sendToUrl);
  36. //地址重写
  37. app.Context.RewritePath(sendToUrl);
  38.  
  39. //Temp code for debug
  40. //using (StreamWriter sw = new StreamWriter(@"c:\test\rr.txt", true, System.Text.Encoding.UTF8))
  41. //{
  42. // sw.WriteLine(app.Request.Url.ToString());
  43. // sw.WriteLine("--------------------------------------");
  44. // sw.Close();
  45. //}
  46.  
  47. //退出for循环
  48. break;
  49. }
  50. }
  51.  
  52. //结束跟踪日志
  53. app.Context.Trace.Write("ModuleRewriter", "Exiting ModuleRewriter");
  54. }
  55.  
  56. }//end class
  57. }

最后在web.config中注册自定义的Http模块:

  1. <httpModules>
  2. <add name="ModuleRewriter" type="PaoTiao.PTRewriter.ModuleRewriter, PaoTiao.PTRewriter"/>
  3. </httpModules>

应用

回到前面的示例, http://wu-jian.cnblogs.com --> /wu-jian/default.aspx

wu-jian所在的位置为域名前缀,或叫二级域名,这就需要在DNS上做一个 *.cnblogs.com 的泛解析。

第二个示例是将目录解析到某一地址:http://www.cnblogs.com/wu-jian --> /test/url.aspx?p=wu-jian

很明显,这里的关键点在于怎样让IIS把这种格式的请求交由.net进程来处理,一旦进入.net framwork,我们就能随心所欲了。OK,通过如下的操作过程即可:

IIS管理-->站点-->属性-->主目录标签-->配置-->通配符应用程序映射-->插入

1、选择 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll

2、不勾选“确认文件是否存在”

总结

关于Url Rewrite的介绍很多,第三方组件也很多,比如isapi rewrite、比如iirf,他们通过IIS底层接口实现所以效率更高,Url Rewriter在.Net进程内实现,也是微软在Framework1.1时代提供的解决方案,其实这篇文章也是多年前写的,只是最近准备换用IIRF,看着那些在.Net Framework4.0中提示已过期的方法,不得不感叹时光流水,对以前曾花了时间和精力的东西做个整理和备忘吧,同时希望给有需要的人带来帮助。

Url路径重写的原理的更多相关文章

  1. django url路径与模板中样式相对路径的问题

    static目录下有css和js及image等文件夹,里面放置网站的一些静态文件,static位于网站根目录下,django中配置静态文件这个就细说,网上都有,昨天在添加新内容时发现一个问题,我的ur ...

  2. 我心中的核心组件~HttpHandler和HttpModule实现图像的缩放与Url的重写

    回到目录 说在前 对于资源列表页来说,我们经常会把图像做成N多种,大图,小图,中图等等,很是麻烦,在数据迁移时,更是一种痛快,而如果你把图像资源部署到nginx上,那么这种图像缩放就变得很容易了,因为 ...

  3. paip.解决中文url路径的问题图片文件不能显示

    paip.解决中文url路径的问题图片文件不能显示 #现状..中文url路径 图片文件不能显示 <img src="img/QQ截图20140401175433.jpg" w ...

  4. ASP.NET URL伪静态重写实现方法

    ASP.NET URL伪静态重写实现方法 首先说下,ASP.NET URL伪静态只是将~/a_1.html指向到了~/a.aspx?ID=1,但a.aspx还是真实存在的,你不用./a_1.html来 ...

  5. SVN 修改URL路径

    http://strugglelinux.blog.51cto.com/1009905/672008 标签:休闲 SVN 修改URL路径 职场 原创作品,允许转载,转载时请务必以超链接形式标明文章 原 ...

  6. JS分页 + 获取MVC地址栏URL路径的最后参数

    @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport&quo ...

  7. jsp中如何获得url路径和绝对路径

    jsp中如何获得url路径 request.getHeader("referer") JSP中获得当前应用的相对路径和绝对路径 根目录所对应的绝对路径:request.getReq ...

  8. Spring—请求映射之URL路径映射

    Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类.并且提供了一组强大的注解:需要通过处理器映射DefaultAnnotati ...

  9. Ajax请求URL后加随机数原理

    原文:Ajax请求URL后加随机数原理 例如: $.ajax({             type: "GET",    url: "login.action?ran=& ...

随机推荐

  1. 前端工具-Sublime、WebStorm-快捷方式使用

    记录下我工作中使用的编辑软件Sublime和WebStorm用到的快捷方式来水一贴(*^__^*) Sublime是我使用的最长时间的编辑器了,也熟悉了一些快捷键使用. 1.Ctrl + /  --- ...

  2. 深入理解kmp中的next数组

    next数组 1. 如果对于值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,相当于next[j] = k. 此意味着什么呢?究其本质,next[j] = ...

  3. jquery的checkbox 全选和全不选

    今天写了一个checkbox的全选和全不选的功能: var check_all=function(){ if(this.checked){ //alert($(".adv_check_num ...

  4. MongoDB安装及添加到Windows服务,随系统启动

    本文介绍在Windows环境下安装MongoDB及添加到Windows服务中,随系统启动 首先去官网下载Windows安装包:https://www.mongodb.org/downloads 一般情 ...

  5. (zhuan) Deep Reinforcement Learning Papers

    Deep Reinforcement Learning Papers A list of recent papers regarding deep reinforcement learning. Th ...

  6. Qt QAxObject操作excel文件过程总结(转):

    正好同事问道Qt下操作excel. 转自:http://blog.csdn.net/a156392343/article/details/48092515 配制方面: 1.确保Excel软件在本地服务 ...

  7. 怎么使用CDR中排列对象功能

    通过将对象发送到其他对象的前面或者后面,可以更改图层或页面上对象的堆叠顺序,还可以将对象按堆叠顺序精确定位,并且可以反转多个对象的堆叠顺序.本教程将详解CorelDRAW中排列对象各按钮功能. 1. ...

  8. SQL Server 优化-执行计划

    对于SQL Server的优化来说,优化查询可能是很常见的事情.由于数据库的优化,本身也是一个涉及面比较的广的话题, 因此本文只谈优化查询时如何看懂SQL Server查询计划.毕竟我对SQL Ser ...

  9. PHP foreach使用

    <?php $arr = array("1"=>"100","2"=>"200","3&qu ...

  10. 支持无限精度无限大数的类BigNumber实现

    介绍 本篇是MathAssist的第二篇,在前言中粗略地展示了MathAssist的“计算和证明”能力,本篇开始将详细介绍其实现原理. 从计算开始说起,要实现任意大数的计算器首先得有一个类支持大数运算 ...