ASP.NET实现URL映射
随着Internet的发展规律, 许多网站开始越来越注重URL的语义性. 即URL符合人们的理解习惯. 比如我要查看某个用户的博客地址:
http://kimiya25/spaces.live.com?type=archive&year=2007&month=12这样的URL可读性较差, 因为一般用户并不完全理解. 如果URL是http://kimiya25/spaces.live.com/archive/2007/12/ 则可读性变强, 用户知道如何查看2007年12月份的文章. 显然我们不能为某个这样的情况都创建一个符合规则的页面, 所有这样复杂的情况依然使用同一个规则. 通过Url Mapping来进行所谓的匹配处理.
ASP.NET默认提供了UrlMapping, 我们在可以Web.config的System.web中定义urlMappings配置节.
现在如果我访问自己的本地站点. 只要输入相应的域名/Default/2007/09就等同于/Default.aspx?year=2007&month=09
ASP.NET应用程序的HttpApplication对象被创建后会读取urlMapping节中的信息, 再用UrlMappingSection类分析出映射的地址列表. 如果匹配成功, 则调用HttpContext.RewritePath方法重写路径. 以达到映射目的, 但它有一个缺点就是只能够一条路径一条的对应, 却不可以以一种一种的方式. 如果当需要映射的规则相待复杂时, 根本无法面对需求.
解决的方法就是自己编写实现IHttpModule的自定义HttpModule. 并且编写自定义的ConfigurationSection来读取自定义的映射规则. 通过为HttpContext.BeginRequest添加世家处理来完成Url映射.
以下为实现工程:
RegxUrlMappingModule实现了IHttpModule接口, 读取相应的配置结, 通过RegexUrlMappingsSection进行管理. 然后通过正则匹配决定是否重写Url. RegexUrlMapping为基本配置元素的实现, RegexUrlMappingCollection则是相应的集合实现.
RegexUrlMapping提供两个配置属性, url 和 mappedUrl, 它派生于ConfigurationElement. 继承关系可以参照以下图:
关于配置节的创建小弟也不多说了, 相对比较简单, 你只需要继承相应的配置类, 然后实现一些必要的方法就可以. 然后在Web.config中注册相应的配置节与程序集信息. (配置节名称和类型)
然后配置相应的正则匹配规则.
因为在编写Section时我们指定了DefaultCollection, 所以对于以上的配置信息, 其实调用了我们编写的Collection, 对于Add里面则调用了RegexUrlMapping的构造函数.
new ConfigurationProperty(null, typeof(RegexUrlMappingCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);
在Section中编写实现了方法HttpResolveMapping, 它遍历节点调用RegexUrlMapping的MatchAndReplace方法判断是否匹配. 匹配则修改传递引用的path.
public string HttpResolveMapping(string path){
foreach (RegexUrlMapping mapper in UrlMappings){
if (mapper.MatchAndReplace(ref path))
break;
}
return path;
}
foreach (RegexUrlMapping mapper in UrlMappings){
if (mapper.MatchAndReplace(ref path))
break;
}
return path;
}
最后来看RegexUrlMappingModule的核心实现.
通过ConfigurationManager的GetSection方法获得自定义的配置节.
ConfigurationManager.GetSection(RegexUrlMappingsSection.SECTION_NAME) as RegexUrlMappingsSection;
在Init时为HttpApplication.BeginRequet绑定事件
public void Init(HttpApplication context){
context.BeginRequest += new EventHandler(context_BeginRequest);
}
context.BeginRequest += new EventHandler(context_BeginRequest);
}
context_BeginRequest负责调用Section的HttpResolveMapping进行当前url的匹配处理, 然后比较是否和原来的url相同, 如果不同则调用HttpContext的RewritePath方法进行了重写.
以上面为例, 现在我输入/2007/10/Default.aspx默认就是等于访问/Default.aspx?year=2007&month=10.
以下为所有实现代码:
Disposable.cs
using System;
namespace Beyondbit.App.UrlMappings
{
public abstract class Disposable : MarshalByRefObject, IDisposable{
private volatile bool _isDisposed = false;
{
public abstract class Disposable : MarshalByRefObject, IDisposable{
private volatile bool _isDisposed = false;
public bool IsDisposed{
get{ return _isDisposed; }
}
get{ return _isDisposed; }
}
protected virtual void Dispose(bool disposing){
if (!_isDisposed) {
_isDisposed = true;
if (disposing) GC.SuppressFinalize(this);
}
}
if (!_isDisposed) {
_isDisposed = true;
if (disposing) GC.SuppressFinalize(this);
}
}
/*protected void Free(){
if (_isDisposed)
throw new ObjectDisposedException(GetType().FullName);
}*/
if (_isDisposed)
throw new ObjectDisposedException(GetType().FullName);
}*/
void IDisposable.Dispose(){
Dispose(true);
}
Dispose(true);
}
public void Dispose(){
Dispose(true);
}
}
}
Dispose(true);
}
}
}
RegexUrlMapping.cs
using System;
using System.Configuration;
using System.Web;
using System.Web.Util;
using System.Text.RegularExpressions;
using System.Configuration;
using System.Web;
using System.Web.Util;
using System.Text.RegularExpressions;
namespace Beyondbit.App.UrlMappings
{
/// <summary>
/// 正则UrlMapping元素
/// </summary>
public class RegexUrlMapping : ConfigurationElement{
static readonly ConfigurationProperty _propMappedUrl;
static readonly ConfigurationProperty _propUrl;
static readonly WhiteSpaceTrimStringConverter _trimConverter = new WhiteSpaceTrimStringConverter();
static readonly StringValidator _nonEmptyStringValidator = new StringValidator(1);
{
/// <summary>
/// 正则UrlMapping元素
/// </summary>
public class RegexUrlMapping : ConfigurationElement{
static readonly ConfigurationProperty _propMappedUrl;
static readonly ConfigurationProperty _propUrl;
static readonly WhiteSpaceTrimStringConverter _trimConverter = new WhiteSpaceTrimStringConverter();
static readonly StringValidator _nonEmptyStringValidator = new StringValidator(1);
static ConfigurationPropertyCollection _properties;
static RegexUrlMapping(){
_propUrl = new ConfigurationProperty("url", typeof(string), null, _trimConverter, _nonEmptyStringValidator, ConfigurationPropertyOptions.IsKey | ConfigurationPropertyOptions.IsRequired);
_propMappedUrl = new ConfigurationProperty("mappedUrl", typeof(string));
_properties = new ConfigurationPropertyCollection();
_properties.Add(_propUrl);
_properties.Add(_propMappedUrl);
}
_propUrl = new ConfigurationProperty("url", typeof(string), null, _trimConverter, _nonEmptyStringValidator, ConfigurationPropertyOptions.IsKey | ConfigurationPropertyOptions.IsRequired);
_propMappedUrl = new ConfigurationProperty("mappedUrl", typeof(string));
_properties = new ConfigurationPropertyCollection();
_properties.Add(_propUrl);
_properties.Add(_propMappedUrl);
}
public RegexUrlMapping(){ }
public RegexUrlMapping(string url, string mappedUrl){
base[_propUrl] = url;
base[_propMappedUrl] = mappedUrl;
}
base[_propUrl] = url;
base[_propMappedUrl] = mappedUrl;
}
[ConfigurationProperty("url", IsRequired = true, IsKey = true)]
public string Url{
get { return base[_propUrl] as string; }
}
public string Url{
get { return base[_propUrl] as string; }
}
[ConfigurationProperty("mappedUrl", IsRequired = true)]
public string MappedUrl{
get { return base[_propMappedUrl] as string; }
}
public string MappedUrl{
get { return base[_propMappedUrl] as string; }
}
protected override ConfigurationPropertyCollection Properties{
get { return _properties; }
}
get { return _properties; }
}
/// <summary>
/// 匹配url
/// 替换引用的path值
/// </summary>
public bool MatchAndReplace(ref string path){
bool flag = false;
Regex regex = new Regex(this.Url, RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (regex.IsMatch(path)){
path = regex.Replace(path, MappedUrl);
flag = true;
}
return flag;
}
/// 匹配url
/// 替换引用的path值
/// </summary>
public bool MatchAndReplace(ref string path){
bool flag = false;
Regex regex = new Regex(this.Url, RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (regex.IsMatch(path)){
path = regex.Replace(path, MappedUrl);
flag = true;
}
return flag;
}
/*static void ValidateUrl(Object value){
_nonEmptyStringValidator.Validate(value);
string url = value as string;
_nonEmptyStringValidator.Validate(value);
string url = value as string;
if (!IsAppRelativePath(url)){
throw new ConfigurationErrorsException("只允许应用程序相对 URL (~/url)。");
}
}
throw new ConfigurationErrorsException("只允许应用程序相对 URL (~/url)。");
}
}
static bool IsAppRelativePath(string path){
if (path == null){
return false;
}
int num1 = path.Length;
if (num1 == 0){
return false;
}
if (path[0] != '~'){
return false;
}
if ((num1 != 1) && (path[1] != '\\')){
return (path[1] == '/');
}
return true;
}*/
}
}
if (path == null){
return false;
}
int num1 = path.Length;
if (num1 == 0){
return false;
}
if (path[0] != '~'){
return false;
}
if ((num1 != 1) && (path[1] != '\\')){
return (path[1] == '/');
}
return true;
}*/
}
}
RegexUrlMappingCollection.cs
using System;
using System.Configuration;
using System.Reflection;
using System.Web.Util;
using System.Configuration;
using System.Reflection;
using System.Web.Util;
namespace Beyondbit.App.UrlMappings
{
/// <summary>
/// 正则UrlMapping元素集合
/// </summary>
public class RegexUrlMappingCollection : ConfigurationElementCollection{
private static readonly ConfigurationPropertyCollection _properties;
{
/// <summary>
/// 正则UrlMapping元素集合
/// </summary>
public class RegexUrlMappingCollection : ConfigurationElementCollection{
private static readonly ConfigurationPropertyCollection _properties;
static RegexUrlMappingCollection(){
_properties = new ConfigurationPropertyCollection();
}
_properties = new ConfigurationPropertyCollection();
}
public RegexUrlMappingCollection()
: base(StringComparer.OrdinalIgnoreCase){
}
: base(StringComparer.OrdinalIgnoreCase){
}
public string[] AllKeys {
get { return ObjectArrayToStringArray(base.BaseGetAllKeys()); }
}
get { return ObjectArrayToStringArray(base.BaseGetAllKeys()); }
}
public RegexUrlMapping this[int index] {
get { return (RegexUrlMapping)base.BaseGet(index); }
set {
if (base.BaseGet(index) != null){
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
get { return (RegexUrlMapping)base.BaseGet(index); }
set {
if (base.BaseGet(index) != null){
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
public new RegexUrlMapping this[string name] {
get { return base.BaseGet(name) as RegexUrlMapping; }
}
get { return base.BaseGet(name) as RegexUrlMapping; }
}
protected override ConfigurationPropertyCollection Properties {
get { return _properties; }
}
get { return _properties; }
}
/// <summary>
/// 添加元素
/// </summary>
public void Add(RegexUrlMapping urlMapping){
this.BaseAdd(urlMapping);
}
/// 添加元素
/// </summary>
public void Add(RegexUrlMapping urlMapping){
this.BaseAdd(urlMapping);
}
/// <summary>
/// 清除
/// </summary>
public void Clear(){
base.BaseClear();
}
/// 清除
/// </summary>
public void Clear(){
base.BaseClear();
}
/// <summary>
/// 创建配置节点
/// </summary>
protected override ConfigurationElement CreateNewElement(){
return new RegexUrlMapping();
}
/// 创建配置节点
/// </summary>
protected override ConfigurationElement CreateNewElement(){
return new RegexUrlMapping();
}
/// <summary>
/// 获得元素键
/// </summary>
protected override object GetElementKey(ConfigurationElement element){
return (element as RegexUrlMapping).Url;
}
/// 获得元素键
/// </summary>
protected override object GetElementKey(ConfigurationElement element){
return (element as RegexUrlMapping).Url;
}
/// <summary>
/// 获得键
/// </summary>
public string GetKey(int index){
return base.BaseGetKey(index) as string;
}
/// 获得键
/// </summary>
public string GetKey(int index){
return base.BaseGetKey(index) as string;
}
/// <summary>
/// 移除
/// </summary>
public void Remove(string name){
base.BaseRemove(name);
}
/// 移除
/// </summary>
public void Remove(string name){
base.BaseRemove(name);
}
/// <summary>
/// 键移除
/// </summary>
public void Remove(RegexUrlMapping urlMapping){
base.BaseRemove(GetElementKey(urlMapping));
}
/// 键移除
/// </summary>
public void Remove(RegexUrlMapping urlMapping){
base.BaseRemove(GetElementKey(urlMapping));
}
/// <summary>
/// 索引移除
/// </summary>
public void RemoveAt(int index){
base.BaseRemoveAt(index);
}
/// 索引移除
/// </summary>
public void RemoveAt(int index){
base.BaseRemoveAt(index);
}
/// <summary>
///
/// </summary>
/// <param name="objectArray"></param>
static string[] ObjectArrayToStringArray(object[] objectArray){
string[] textArray1 = new string[objectArray.Length];
objectArray.CopyTo(textArray1, 0);
return textArray1;
}
}
}
///
/// </summary>
/// <param name="objectArray"></param>
static string[] ObjectArrayToStringArray(object[] objectArray){
string[] textArray1 = new string[objectArray.Length];
objectArray.CopyTo(textArray1, 0);
return textArray1;
}
}
}
RegexUrlMappingsSection.cs
using System;
using System.Configuration;
using System.Web.Util;
using System.Text.RegularExpressions;
using System.Configuration;
using System.Web.Util;
using System.Text.RegularExpressions;
namespace Beyondbit.App.UrlMappings
{
/// <summary>
/// 正则UrlMapping配置节
/// </summary>
public class RegexUrlMappingsSection : ConfigurationSection{
public const string SECTION_NAME = "RegexUrlMappings";
{
/// <summary>
/// 正则UrlMapping配置节
/// </summary>
public class RegexUrlMappingsSection : ConfigurationSection{
public const string SECTION_NAME = "RegexUrlMappings";
static readonly ConfigurationProperty _propEnabled;
/// <summary>
/// 是否重置虚拟路径
/// </summary>
static readonly ConfigurationProperty _propRebaseClientPath;
static readonly ConfigurationProperty _propMappings;
/// <summary>
/// 是否重置虚拟路径
/// </summary>
static readonly ConfigurationProperty _propRebaseClientPath;
static readonly ConfigurationProperty _propMappings;
static ConfigurationPropertyCollection _properties;
static RegexUrlMappingsSection(){
_propEnabled = new ConfigurationProperty("enabled", typeof(bool), true, ConfigurationPropertyOptions.None);
_propRebaseClientPath = new ConfigurationProperty("rebaseClientPath", typeof(bool), true, ConfigurationPropertyOptions.None);
_propMappings = new ConfigurationProperty(null, typeof(RegexUrlMappingCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);
_properties = new ConfigurationPropertyCollection();
_properties.Add(_propEnabled);
_properties.Add(_propRebaseClientPath);
_properties.Add(_propMappings);
}
static RegexUrlMappingsSection(){
_propEnabled = new ConfigurationProperty("enabled", typeof(bool), true, ConfigurationPropertyOptions.None);
_propRebaseClientPath = new ConfigurationProperty("rebaseClientPath", typeof(bool), true, ConfigurationPropertyOptions.None);
_propMappings = new ConfigurationProperty(null, typeof(RegexUrlMappingCollection), null, ConfigurationPropertyOptions.IsDefaultCollection);
_properties = new ConfigurationPropertyCollection();
_properties.Add(_propEnabled);
_properties.Add(_propRebaseClientPath);
_properties.Add(_propMappings);
}
[ConfigurationProperty("enabled", DefaultValue = true)]
public bool IsEnabled{
get { return (bool)base[_propEnabled]; }
set { base[_propEnabled] = value; }
}
public bool IsEnabled{
get { return (bool)base[_propEnabled]; }
set { base[_propEnabled] = value; }
}
[ConfigurationProperty("rebaseClientPath", DefaultValue = true)]
public bool RebaseClientPath{
get { return (bool)base[_propRebaseClientPath]; }
set { base[_propRebaseClientPath] = value; }
}
public bool RebaseClientPath{
get { return (bool)base[_propRebaseClientPath]; }
set { base[_propRebaseClientPath] = value; }
}
protected override ConfigurationPropertyCollection Properties{
get { return _properties; }
}
get { return _properties; }
}
[ConfigurationProperty("", IsDefaultCollection = true)]
public RegexUrlMappingCollection UrlMappings{
get { return base[_propMappings] as RegexUrlMappingCollection; }
}
public RegexUrlMappingCollection UrlMappings{
get { return base[_propMappings] as RegexUrlMappingCollection; }
}
public string HttpResolveMapping(string path){
foreach (RegexUrlMapping mapper in UrlMappings){
if (mapper.MatchAndReplace(ref path))
break;
}
return path;
}
}
}
foreach (RegexUrlMapping mapper in UrlMappings){
if (mapper.MatchAndReplace(ref path))
break;
}
return path;
}
}
}
RegexUrlMappingModule.cs
using System;
using System.Configuration;
using System.Web;
using System.Configuration;
using System.Web;
namespace Beyondbit.App.UrlMappings
{
public class RegexUrlMappingModule : IHttpModule{
static RegexUrlMappingsSection _settings = null;
static bool _retrieved = false; //是否检索过
{
public class RegexUrlMappingModule : IHttpModule{
static RegexUrlMappingsSection _settings = null;
static bool _retrieved = false; //是否检索过
RegexUrlMappingsSection Settings{
get {
if (!_retrieved){
_settings = ConfigurationManager.GetSection(RegexUrlMappingsSection.SECTION_NAME) as RegexUrlMappingsSection;
_retrieved = true;
}
return _settings;
}
}
get {
if (!_retrieved){
_settings = ConfigurationManager.GetSection(RegexUrlMappingsSection.SECTION_NAME) as RegexUrlMappingsSection;
_retrieved = true;
}
return _settings;
}
}
#region IHttpModule 成员
public void Dispose(){ }
public void Init(HttpApplication context){
context.BeginRequest += new EventHandler(context_BeginRequest);
}
context.BeginRequest += new EventHandler(context_BeginRequest);
}
/// <summary>
/// BeginRequest事件
/// 根据Web.config中的Url映射规则重写HTTP请求的地址
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void context_BeginRequest(object sender, EventArgs e){
HttpApplication application = sender as HttpApplication;
HttpContext context = application.Context;
string currentPath = context.Request.Url.PathAndQuery;
if (this.Settings != null){
if (_settings.IsEnabled){
string modifiedPath = Settings.HttpResolveMapping(currentPath);
if (!String.Equals(currentPath, modifiedPath)){
context.RewritePath(modifiedPath, _settings.RebaseClientPath);
}
}
}
}
/// BeginRequest事件
/// 根据Web.config中的Url映射规则重写HTTP请求的地址
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void context_BeginRequest(object sender, EventArgs e){
HttpApplication application = sender as HttpApplication;
HttpContext context = application.Context;
string currentPath = context.Request.Url.PathAndQuery;
if (this.Settings != null){
if (_settings.IsEnabled){
string modifiedPath = Settings.HttpResolveMapping(currentPath);
if (!String.Equals(currentPath, modifiedPath)){
context.RewritePath(modifiedPath, _settings.RebaseClientPath);
}
}
}
}
#endregion
}
}
}
}