Url路径重写的原理
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重写。
自定义节点的声明:
<configSections>
<section name="RewriterConfig"
type="PaoTiao.PTRewriter.Config.RewriterConfigSerializerSectionHandler, PaoTiao.PTRewriter"/>
</configSections>
自定义节点配置项:
<RewriterConfig>
<Rules>
<RewriterRule>
<LookFor>^http://([a-zA-Z0-9]{4,16}).cnblogs.com/default.aspx$</LookFor>
<SendTo>/$1/default.aspx</SendTo>
</RewriterRule> <RewriterRule>
<LookFor>^http://www.cnblogs.com/([a-zA-Z0-9]{4,16})/$</LookFor>
<SendTo>/test/url.aspx?p=$1</SendTo>
</RewriterRule>
</Rules>
</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中的一段:
<RewriterRule>
<LookFor>~/(\d{4})/(\d{2})/(\d{2})\.aspx</LookFor>
<SendTo>~/ShowBlogContent.aspx?year=$1&month=$2&day=$3</SendTo>
</RewriterRule>
可以发现,当需要使用二级域名或自定义级别更高的rewrite时,URLRewriter是不支持的,所以在此我将源代码作了一小部分优化,匹配与重写都使用LookFor和SendTo中的原始表达式,不做任何智能替换与修改。其实很多时候,在微软的产品中都能发现这种“画蛇添足”的影子。
关于匹配与替换, 其实就是应用了正则表达式中的“反向引用”原理,在我的博客里有代码示例,不熟悉正则的朋友可去了解,此处不作详叙。
源代码分析
对自定义配置进行访问的类:
using System;
using System.Configuration;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath; namespace PaoTiao.PTRewriter.Config
{
///<summary>
/// 实现IConfigurationSectionHandler接口,以对自定义节点进行访问
///</summary>
publicclass RewriterConfigSerializerSectionHandler : IConfigurationSectionHandler
{
///<summary>
/// 该方法无需主动调用
/// 它在ConfigurationManager.GetSection()被调用时根据改配置节声明中所定义的类名和路径自动实例化配置节处理类
///</summary>
publicobject Create(object parent, object configContext, System.Xml.XmlNode section)
{
XmlSerializer ser =new XmlSerializer(typeof(RewriterConfiguration));
return ser.Deserialize(new XmlNodeReader(section));
} }//end class
}
之前一直写WEB程序,很少用到自定义节点,直到一次写Windows Service用到了app.config,发现要读取自定义节点,就需要实现IConfigurationSectionHandler接口。
using System;
using System.Web;
using System.Web.Caching;
using System.Configuration;
using System.Xml.Serialization; namespace PaoTiao.PTRewriter.Config
{
[Serializable()]
[XmlRoot("RewriterConfig")]
publicclass RewriterConfiguration
{
private RewriterRuleCollection rules; ///<summary>
/// 该方法从web.config中读取规则集合,并使用了Cache以避免频繁IO操作
///</summary>
///<returns></returns>
publicstatic RewriterConfiguration GetConfig()
{
//使用缓存
if (HttpContext.Current.Cache["RewriterConfig"] ==null)
HttpContext.Current.Cache.Insert("RewriterConfig", ConfigurationManager.GetSection("RewriterConfig")); return (RewriterConfiguration)HttpContext.Current.Cache["RewriterConfig"];
} public RewriterRuleCollection Rules
{
get { return rules; }
set { rules = value; }
} }//end class
}
我想使用UrlRewrite的站点绝大部分都是面向公众用户的,面向公众用户就面临着大的流量和并发,谁也不愿意为每个请求去读取一次web.config吧,那么在此处使用Cache是明智之举。另外更换了已过期的ConfigurationSettings.GetConfig()方法为ConfigurationManager.GetSection()方法。
如下两个类完成类似的Model功能。
using System;
using System.Collections; namespace PaoTiao.PTRewriter.Config
{
///<summary>
/// 规则集合
///</summary>
[Serializable()]
publicclass RewriterRuleCollection : CollectionBase
{
///<summary>
/// 向集合中添加新规则
///</summary>
///<param name="r">RewriterRule对象</param>
publicvirtualvoid Add(RewriterRule r)
{
this.InnerList.Add(r);
} ///<summary>
/// 获取或设置项
///</summary>
public RewriterRule this[int index]
{
get { return (RewriterRule)this.InnerList[index]; }
set { this.InnerList[index] = value; }
} }//end class
}
using System; namespace PaoTiao.PTRewriter.Config
{
///<summary>
/// 重写规则的数据对象
///</summary>
[Serializable()]
publicclass RewriterRule
{
privatestring mLookFor;
privatestring mSendTo; ///<summary>
/// 查找规则
///</summary>
publicstring LookFor{
get { returnthis.mLookFor; }
set { this.mLookFor = value; }
} ///<summary>
/// 重写规则
///</summary>
publicstring SendTo{
get { returnthis.mSendTo; }
set { this.mSendTo = value; }
} }//end class
}//end namespace
使用HttpModule实现地址重写
using System;
using System.Web; namespace PaoTiao.PTRewriter
{
///<summary>
/// 实现IHttpModule的抽象类
///</summary>
publicabstractclass BaseModuleRewriter : IHttpModule
{
publicvirtualvoid Dispose() { } publicvirtualvoid Init(HttpApplication app)
{
app.BeginRequest +=new EventHandler(this.BaseModuleRewriter_BeginRequest);
} protectedvirtualvoid BaseModuleRewriter_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
Rewrite(app);
} ///<summary>
/// 地址重写抽象函数
///</summary>
///<param name="app"></param>
protectedabstractvoid Rewrite(HttpApplication app); }//end class
}
在Http模块中进行核心逻辑处理,源代码是在AuthorizeRequest事件中,此处我使用了BeginRequest事件。
对抽象方法Rewrite的实现。大家可以发现URL重写其实就一个核心方法:HttpContext.RewritePath
看看MSDN中对该方法的描述:指定内部重写路径,并允许请求的 URL 与资源的内部路径不同。
using System;
using System.Text.RegularExpressions;
using System.Configuration; using System.IO; namespace PaoTiao.PTRewriter
{
publicclass ModuleRewriter : BaseModuleRewriter
{
///<summary>
/// 地址重写函数
///</summary>
///<param name="app"></param>
protectedoverridevoid Rewrite(System.Web.HttpApplication app)
{
//开始跟踪日志
app.Context.Trace.Write("ModuleRewriter", "Entering ModuleRewriter"); //获取规则集合
Config.RewriterRuleCollection rules = Config.RewriterConfiguration.GetConfig().Rules; for (int i =; i < rules.Count; i++)
{
string lookFor = rules[i].LookFor;
Regex reg =new Regex(lookFor, RegexOptions.IgnoreCase); if (reg.IsMatch(app.Request.Url.ToString()))
{
//获取目的URL
string sendToUrl = reg.Replace(app.Request.Url.ToString(), rules[i].SendTo); //跟踪日志
app.Context.Trace.Write("ModuleRewriter", "Rewriting URL to "+ sendToUrl);
//地址重写
app.Context.RewritePath(sendToUrl); //Temp code for debug
//using (StreamWriter sw = new StreamWriter(@"c:\test\rr.txt", true, System.Text.Encoding.UTF8))
//{
// sw.WriteLine(app.Request.Url.ToString());
// sw.WriteLine("--------------------------------------");
// sw.Close();
//} //退出for循环
break;
}
} //结束跟踪日志
app.Context.Trace.Write("ModuleRewriter", "Exiting ModuleRewriter");
} }//end class
}
最后在web.config中注册自定义的Http模块:
<httpModules>
<add name="ModuleRewriter" type="PaoTiao.PTRewriter.ModuleRewriter, PaoTiao.PTRewriter"/>
</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路径重写的原理的更多相关文章
- django url路径与模板中样式相对路径的问题
static目录下有css和js及image等文件夹,里面放置网站的一些静态文件,static位于网站根目录下,django中配置静态文件这个就细说,网上都有,昨天在添加新内容时发现一个问题,我的ur ...
- 我心中的核心组件~HttpHandler和HttpModule实现图像的缩放与Url的重写
回到目录 说在前 对于资源列表页来说,我们经常会把图像做成N多种,大图,小图,中图等等,很是麻烦,在数据迁移时,更是一种痛快,而如果你把图像资源部署到nginx上,那么这种图像缩放就变得很容易了,因为 ...
- paip.解决中文url路径的问题图片文件不能显示
paip.解决中文url路径的问题图片文件不能显示 #现状..中文url路径 图片文件不能显示 <img src="img/QQ截图20140401175433.jpg" w ...
- ASP.NET URL伪静态重写实现方法
ASP.NET URL伪静态重写实现方法 首先说下,ASP.NET URL伪静态只是将~/a_1.html指向到了~/a.aspx?ID=1,但a.aspx还是真实存在的,你不用./a_1.html来 ...
- SVN 修改URL路径
http://strugglelinux.blog.51cto.com/1009905/672008 标签:休闲 SVN 修改URL路径 职场 原创作品,允许转载,转载时请务必以超链接形式标明文章 原 ...
- JS分页 + 获取MVC地址栏URL路径的最后参数
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport&quo ...
- jsp中如何获得url路径和绝对路径
jsp中如何获得url路径 request.getHeader("referer") JSP中获得当前应用的相对路径和绝对路径 根目录所对应的绝对路径:request.getReq ...
- Spring—请求映射之URL路径映射
Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类.并且提供了一组强大的注解:需要通过处理器映射DefaultAnnotati ...
- Ajax请求URL后加随机数原理
原文:Ajax请求URL后加随机数原理 例如: $.ajax({ type: "GET", url: "login.action?ran=& ...
随机推荐
- python内建函数
人太懒了,博客就慢慢添加吧,这个话题还有很多要学的,后面实践了再来添加,现在就当是开个头. print(dir(__builtins__)) #获取内建属性.函数列表 print(help( ...
- fail to create java virtual machine..
今天打开zend stdio 的时候 出现的错误 fail to create java virtual machine... 然后找度娘了,,都说改xxxxx, 我打开360 ,把内存清理了一遍 ...
- Document树的解析方法
一.本次总结用到的xml文本 1. <?xml version="1.0" encoding="UTF-8" standalone="no ...
- CSS3+JS切割轮播图
以下说明数据,是指有4张图片的轮播图,分别切割成4张. 首先,做成单张切换的立体效果,即通过旋转,确定四张图片的位置,分别是一个立方体的上下前后的图片翻转移动角度. .box ul li:nth-ch ...
- OS实验报告--FCFS算法
实验二.作业调度模拟实验 专业:商业软件工程 姓名:王泽锴 学号:201406114113 一.实验目的 (1)加深对作业调度算法的理解: (2)进行程序设计的训练. 二.实验内容和要求 (1)实验 ...
- HTML中的属性、段落、标题、换行等
HTML的属性html标签的属性总是以名称/值(name=“value”)的方式出现:属性总是在html元素的开始标签中规定: 1.html链接由<a>标签定义,url在href中指定:& ...
- EHcache缓存框架详解
EhCache是一个纯Java的进程内缓存框架,具有快速.精干等特点,也是Hibernate中默认的CacheProvider. 归纳一下它大概具有一下几个特点: 1. 快速. 2. 简单. 3. 多 ...
- Web跨域问题总结
一.跨域问题的由来为了防止某些文档或脚本加载别的域下的未知内容造成泄露隐私,破坏系统等安全行为,1995年, Netscape 公司在浏览器中引入同源策略/SOP(Same origin policy ...
- SQL NULL 值【摘自W3C】
SQL NULL 值 本章讲解 IS NULL 和 IS NOT NULL 操作符. NULL 值是遗漏的未知数据.默认地,表的列可以存放 NULL 值. 如果表中的某个列是可选的,那么我们可以在不向 ...
- B 最熟悉的陌生人 (纪念当年就读的梅州市江南高级中学)
最熟悉的陌生人 作者:张慧桥 枪与玫瑰 我看了一下聊天室的名单,哈哈哈,我不禁喜出望外:蝶恋花那丫头片子挂在线上呢,真是天助我也.初时的担心一扫而光,我精神抖擞地喝下一大口咖啡,猛抽了三口烟,现在的我 ...