一起谈.NET技术,ASP.NET的地址重写(URLRewriter)实现原理及代码示例

  一、概述

  访问者输入: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&amp;month=$2&amp;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>
public class RewriterConfigSerializerSectionHandler : IConfigurationSectionHandler
{
/// <summary>
/// 该方法无需主动调用
/// 它在ConfigurationManager.GetSection()被调用时根据改配置节声明中所定义的类名和路径自动实例化配置节处理类
/// </summary>
public object 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")]
public class RewriterConfiguration
{
private RewriterRuleCollection rules;

/// <summary>
/// 该方法从web.config中读取规则集合,并使用了Cache以避免频繁IO操作
/// </summary>
/// <returns></returns>
public static 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()]
public class RewriterRuleCollection : CollectionBase
{
/// <summary>
/// 向集合中添加新规则
/// </summary>
/// <param name="r">RewriterRule对象</param>
public virtual void 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()]
public class RewriterRule
{
private string mLookFor;
private string mSendTo;

/// <summary>
/// 查找规则
/// </summary>
public string LookFor{
get { return this.mLookFor; }
set { this.mLookFor = value; }
}

/// <summary>
/// 重写规则
/// </summary>
public string SendTo{
get { return this.mSendTo; }
set { this.mSendTo = value; }
}

}//end class
}//end namespace

  使用HttpModule实现地址重写:


using System;
using System.Web;

namespace PaoTiao.PTRewriter
{
/// <summary>
/// 实现IHttpModule的抽象类
/// </summary>
public abstract class BaseModuleRewriter : IHttpModule
{
public virtual void Dispose() { }

public virtual void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(this.BaseModuleRewriter_BeginRequest);
}

protected virtual void BaseModuleRewriter_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
Rewrite(app);
}

/// <summary>
/// 地址重写抽象函数
/// </summary>
/// <param name="app"></param>
protected abstract void 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
{
public class ModuleRewriter : BaseModuleRewriter
{
/// <summary>
/// 地址重写函数
/// </summary>
/// <param name="app"></param>
protected override void Rewrite(System.Web.HttpApplication app)
{
//开始跟踪日志
app.Context.Trace.Write("ModuleRewriter", "Entering ModuleRewriter");

//获取规则集合
Config.RewriterRuleCollection rules = Config.RewriterConfiguration.GetConfig().Rules;

for (int i = 0; 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中提示已过期的方法,不得不感叹时光流水,对以前曾花了时间和精力的东西做个整理和备忘吧,同时希望给有需要的人带来帮助。

时间: 2024-11-03 00:54:58

一起谈.NET技术,ASP.NET的地址重写(URLRewriter)实现原理及代码示例的相关文章

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

一.概述 访问者输入:http://wu-jian.cnbolgs.com/default.aspx,实际请求和响应的地址却是:http://www.cnblogs.com/wu-jian/default.aspx, 这就是UrlRewrite,除了实现二级域名功能,它在简化用户输入地址.SEO.网站版本迭代更新等多个方面发挥着重要作用. 微软曾在.net framework 1.1中提供过一个名为URLRewriter的小工具供开发人员轻松实现UrlRewrite,下载地址为:http://d

ASP.NET的地址重写“.NET研究”(URLRewriter)实现原理及代码示例

一.概述 访问者输入:http://wu-jian.cnbolgs.com/default.aspx,实际请求和响应的地址却是:http://www.cnblogs.com/wu-jian/default.aspx, 这就是UrlRewrite,除了实现二级域名功能,它在简化用户输入地址.SEO.网站版本迭代更新等多个方面发挥着重要作用. 微软曾在.net framework 1.1中提供过一个名为URLRewriter的小工具供开发人员轻松实现UrlRewrite,下载地址为:http://d

一起谈.NET技术,服务器日志法网站分析的原理及优缺点

     [前言] 应朋友们的要求,我还是写一篇关于服务器日志法进行网站分析的原理以及它的优缺点是什么.请朋友们注意,网站服务器日志法并不容易进行,初学者,以及在绝大多数情况下,进行以用户行为分析为核心的网站分析,用不到服务器日志法.不过,作为网站分析历史不可分割的一部分以及重要的基础篇章,服务器日志法仍然值得一书.下面的这篇文章也是我要撰写的书中截取的内容(我要快马加鞭快快写了,已经辜负了太多朋友的重托,抱歉抱歉!).      [正文] 网站分析收集数据的方式其实有五.六种之多,我们最常见的

一起谈.NET技术,C#特性Attribute的实际应用之:代码统计分析

日常工作中,需要为程序集提供统计分析: 1:程序集方法数: 2:开发人员数目及各自所开发或REVIEW的方法数: 3:测试中,被标注有BUG的数目: 4:直接查看方法的IL代码: 鉴于以上统计的需要,特开发本EXE. 1:关于特性Attribute的知识补充 "Attribute是一种可由用户自由定义的修饰符(Modifier),可以用来修饰各种需要被修饰的目标".它的作用是为它们的修饰目标追加上一些额外的信息--比如"这个类是我写的"或者"这个函数以前出

asp 根据IP地址自动判断转向分站的代码_应用技巧

Function getIpvalue(clientIP)'得到客户端的IP转换成长整型,返回值getIpvalue On Error Resume Next Dim strIp, array_Ip strIp=0 array_Ip = Split(clientIP,".") If UBound(array_Ip)<>3 Then getIpvalue=0 Exit Function End If For i=0 To 3 strIp=strIp+(CInt(array_I

asp 根据IP地址自动判断转向分站的代码

Function getIpvalue(clientIP)'得到客户端的IP转换成长整型,返回值getIpvalue On Error Resume Next Dim strIp, array_Ip strIp=0 array_Ip = Split(clientIP,".") If UBound(array_Ip)<>3 Then getIpvalue=0 Exit Function End If For i=0 To 3 strIp=strIp+(CInt(array_I

一起谈.NET技术,提高效率 用好Visual Studio 2010自定义代码段

我在演讲时发现这个功能也相当有用,不用现场敲代码,直接调出非常方便.Visual Studio 2010增强了自定义代码段功能,使创建自定义代码段的操作更加简单了. 有两种类型的代码段: ◆在游标中插入的Expansion自定义代码段 ◆围绕选定代码的SurroundsWith自定义代码段 创建自定义代码段 首先在项目中插入一个新的XML文件,取名为TryCatchFinally.snippet,注意文件名的后缀是.snippet,然后在编辑器窗口点击右键,选择"插入代码段"*&quo

一起谈.NET技术,如何通过ildasm/ilasm修改assembly的IL代码

这段时间为跟踪一个Bug而焦头烂额,最后发现是Framework的问题,这让人多少有些绝望.所以到微软论坛提了个帖子,希望能得到些帮助.虽然论坛智能到能够判断楼主是否是MSDN订阅用户,以便尽快解决(传说MSDN订阅用户的问题能在两天内得到回复的,当时还很得意公司为我们购买的MSDN订阅账号),但得到的回复是"Could you file a bug report for this issue through Connect?",绝望之后的又一次寒心啊. 看过payeasy的广告之后,

asp.net用url重写URLReWriter实现任意二级域名 高级篇_实用技巧

 我最近写了个小例子,大家可以先看这个,里面有小例子的完整代码下载 http://www.jb51.net/article/20906.htm 好久没有写技术文章,如果大家看不明白,就多看几篇,汗,或者,在文章的后面回复(这是最有效的办法),我会尽力帮助大家解答疑惑. 来找这篇文章的,应该都知道什么叫二级域名吧,废话就不说了.但是讨论前,先要明白一个思想问题.很多朋友一直考虑不清(我前几天也一直搞不明白)的问题是,我键入一个地址后,怎么这个url就被重写了?第一步:在浏览器键入了一个地址,比如h