惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
The GitHub Blog
The GitHub Blog
Martin Fowler
Martin Fowler
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Help Net Security
Microsoft Security Blog
Microsoft Security Blog
有赞技术团队
有赞技术团队
GbyAI
GbyAI
F
Fortinet All Blogs
The Cloudflare Blog
爱范儿
爱范儿
IT之家
IT之家
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
博客园 - 【当耐特】
博客园_首页
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
量子位
C
Cisco Blogs
Latest news
Latest news
Hugging Face - Blog
Hugging Face - Blog
S
Securelist
Stack Overflow Blog
Stack Overflow Blog
K
Kaspersky official blog
Spread Privacy
Spread Privacy
B
Blog
L
Lohrmann on Cybersecurity
Simon Willison's Weblog
Simon Willison's Weblog
I
Intezer
P
Privacy International News Feed
T
Tor Project blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
小众软件
小众软件
P
Proofpoint News Feed
T
Tailwind CSS Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Recorded Future
Recorded Future
S
Secure Thoughts
人人都是产品经理
人人都是产品经理
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
Last Week in AI
Last Week in AI
W
WeLiveSecurity
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Security Affairs
宝玉的分享
宝玉的分享
D
Docker
Blog — PlanetScale
Blog — PlanetScale
雷峰网
雷峰网

博客园 - 谭洪星

NHibernate教程(转载) MVC模式在.NET框架中的应用与实现 混乱的MVC,.NET非要MVC不可么?(转) PetShop的系统架构设计(转) ASP.NET中常用的优化性能方法 设计模式学习笔记(三)——Abstract Factory抽象工厂模式 (转) 观察者模式及实现(转) 悟透JavaScript (转) 深入研究Asp.net页面的生命周期 [你必须知道的.NET] 第一回:恩怨情仇:is和as Net中的反射使用入门 .Net 中的反射(反射特性) - Part.3 .net反射简介 软件工程心理学之---让客户知错,但不能向你发怒 用AJAX.NET的客户端脚本实现UpdateProgress的效果 - 谭洪星 - 博客园 C#(也适用其他)的初学者对string是值类型还是引用类型搞不清楚,还有对参数传递也比较迷糊 C#泛型之详解 [你必须知道的.NET]第十二回:参数之惑---传递的艺术(下) [你必须知道的.NET]第十一回:参数之惑---传递的艺术(上)
ASP.NET实现URL映射
谭洪星 · 2008-06-16 · via 博客园 - 谭洪星

随着Internet的发展规律, 许多网站开始越来越注重URL的语义性. 即URL符合人们的理解习惯. 比如我要查看某个用户的博客地址:

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;
}

最后来看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负责调用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 bool IsDisposed{
            get{ return _isDisposed; }
        }

        protected virtual void Dispose(bool disposing){
            if (!_isDisposed) {
                _isDisposed = true;
                if (disposing) GC.SuppressFinalize(this);
            }           
        }

        /*protected void Free(){
            if (_isDisposed)
                throw new ObjectDisposedException(GetType().FullName);   
        }*/

        void IDisposable.Dispose(){
            Dispose(true);
        }

        public void Dispose(){
            Dispose(true);
        }
    }
}

RegexUrlMapping.cs

using System;
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);

        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);
        }

        public RegexUrlMapping(){ }

        public RegexUrlMapping(string url, string mappedUrl){
            base[_propUrl] = url;
            base[_propMappedUrl] = mappedUrl;
        }

        [ConfigurationProperty("url", IsRequired = true, IsKey = true)]
        public string Url{
            get { return base[_propUrl] as string; }
        }

        [ConfigurationProperty("mappedUrl", IsRequired = true)]
        public string MappedUrl{
            get { return base[_propMappedUrl] as string; }
        }

        protected override ConfigurationPropertyCollection 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;
        }

        /*static void ValidateUrl(Object value){
            _nonEmptyStringValidator.Validate(value);
            string url = value as string;

            if (!IsAppRelativePath(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;
        }*/
    }
}

RegexUrlMappingCollection.cs

using System;
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;

        static RegexUrlMappingCollection(){
            _properties = new ConfigurationPropertyCollection();
        }

        public RegexUrlMappingCollection()
            : base(StringComparer.OrdinalIgnoreCase){
        }

        public string[] AllKeys {
            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);
            }
        }

        public new RegexUrlMapping this[string name] {
            get { return base.BaseGet(name) as RegexUrlMapping; }
        }

        protected override ConfigurationPropertyCollection Properties {
            get { return _properties; }
        }

        /// <summary>
        /// 添加元素
        /// </summary>
        public void Add(RegexUrlMapping urlMapping){
            this.BaseAdd(urlMapping);
        }

        /// <summary>
        /// 清除
        /// </summary>
        public void Clear(){
            base.BaseClear();
        }

        /// <summary>
        /// 创建配置节点
        /// </summary>
        protected override ConfigurationElement CreateNewElement(){
            return new RegexUrlMapping();
        }

        /// <summary>
        /// 获得元素键
        /// </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>
        /// 移除
        /// </summary>
        public void Remove(string name){
            base.BaseRemove(name);
        }

        /// <summary>
        /// 键移除
        /// </summary>
        public void Remove(RegexUrlMapping urlMapping){
            base.BaseRemove(GetElementKey(urlMapping));
        }

        /// <summary>
        /// 索引移除
        /// </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;
        }
    }
}

RegexUrlMappingsSection.cs

using System;
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";

        static readonly ConfigurationProperty _propEnabled;
        /// <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);
        }

        [ConfigurationProperty("enabled", DefaultValue = true)]
        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; }
        }

        protected override ConfigurationPropertyCollection Properties{
            get { return _properties; }
        }

        [ConfigurationProperty("", IsDefaultCollection = true)]
        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;
        }
    }
}

RegexUrlMappingModule.cs

using System;
using System.Configuration;
using System.Web;

namespace Beyondbit.App.UrlMappings
{
    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;
            }
        }

        #region IHttpModule 成员

        public void Dispose(){ }

        public void Init(HttpApplication context){
            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);
                    }
                }
            }
        }

        #endregion
    }
}