ASP.NET URL双向改写的实现

 

ASP.NET URL双向改写的实现

  我们在进行Web程序开发时,为了进行搜索引擎优化(SEO),往往需要对web的访问地址进行优化,如将http://localhost/Default.aspx?tab=performance修改为http://localhost/Default_performance.aspx,后一个地址能够更好地被搜索引擎搜索到,从而达到了搜索引擎优化的目的。微软有一个开源类库URLRewriter可以非常方便地实现url改写,通过配置在web.config文件中的映射表将用户的请求重定向到具体的页面中,我在“使用URLRewriter进行URL重写失效”一文中详细介绍了如何使用这个类库,该类库是通过asp.net的httpmodules或httphandles来执行的,但如果网站的宿主服务器不支持asp.net httpmodules和httphandles,则该功能便失效了,这时我们可以通过global中的application_beginrequest事件来进行url重定向。本文在URLRewriter类库的基础上进行了改进,并给出了一个相对完整的解决方案。

  我们的改进是建立在URLRewriter的基础之上的,所以URLRewriter原有的东西只要能用,我们都可以直接拿过来,当然,不好的东西要摒弃!

  URLRewriter的映射表是直接写在web.config文件中的,要让web.config能识别映射表,必须在configSections节中添加section,告诉程序如何正确解析web.config中未被识别的内容,如原URLRewriter就需要在web.config中添加<section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter"/>。我觉得这个方式并不好,首先你需要单独去编写一个类库来解析xml,并在web.config中进行配置,我们完全可以省去这一步。url的映射表可以单独写到一个xml文件中,当程序运行时将xml加载到应用程序缓存中,并设置一个缓存文件依赖项,这样每当管理员修改完映射表后就可以马上生效。

  另外我希望支持url的双向改写,即上面提到的两个url,当用户输入第二个url时程序会将请求发送到第一个url,但浏览器中显示的url不变;当用户输入第一个url时,自动跳转到第二个url,此时浏览器中显示的是第二个url,但是请求仍然是第一个url。听起来是不是有点绕啊?没关系,其实也很简单,基本的需求就是说客户原来网站中的很多页面在访问时都带了很多参数,做url改写时都换成新的url了,这时旧的url仍然可以用,客户想的就是当输入原来旧的url时能自动跳转到新的url。这个就是url的双向改写!这两种方式可以分别通过Context.RewritePath()和Context.Response.Redirect()方法来实现,下面我们来看具体的实现。

  首先是映射表的实现。我在URLRewriter原有映射表的基础上做了一点改动,就是给ReWriterRule添加了一个IsDirect属性,该属性可选,默认值为False,当值为真时如果用户请求的url匹配则会进行跳转,否则只是进行请求映射。 

<? xml version="1.0" ?>
< ReWriterConfig  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd ="http://www.w3.org/2001/XMLSchema" >
  
< Rules >
    
< ReWriterRule >
      
< LookFor > ~/Default_(/w+)/.aspx </ LookFor >
      
< SendTo > ~/Default.aspx?tab=$1 </ SendTo >
    
</ ReWriterRule >
    
< ReWriterRule  IsDirect ="true" >
      
< LookFor > ~/Default/.aspx/?tab=(/w+) </ LookFor >
      
< SendTo > ~/Default_$1.aspx </ SendTo >
    
</ ReWriterRule >
  
</ Rules >
</ ReWriterConfig >

  该映射表支持正则表达式,下面是对应的实体类,用来进行反序列化。

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Web;

namespace  URLRewriterTest
{
    [Serializable]
    
public   class  ReWriterConfig
    {
        
public  ReWriterRule[] Rules;
    }

    [Serializable]
    
public   class  ReWriterRule
    {
        
private   bool  _isRedirect  =   false ;

        [System.Xml.Serialization.XmlAttribute(
" IsDirect " )]
        
public   bool  IsRedirect
        {
            
get  {  return  _isRedirect; }
            
set  {  this ._isRedirect  =  value; }
        }
        
public   string  LookFor {  get set ; }
        
public   string  SendTo {  get set ; }
    }
}

  下面这个类用来获取映射表,当程序第一次运行时会将映射表反序列化的结果放到全局应用程序缓存中。

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Web;
using  System.Xml.Serialization;
using  System.IO;
using  System.Web.Caching;

namespace  URLRewriterTest
{
    
public   class  ReWriterConfiguration
    {
        
public   static  ReWriterConfig GetConfig( string  filename)
        {
            
if  (HttpContext.Current.Cache[ " RewriterConfig " ==   null )
            {
                ReWriterConfig config 
=   null ;
                
//  Create an instance of the XmlSerializer specifying type and namespace.   
                XmlSerializer serializer  =   new  XmlSerializer( typeof (ReWriterConfig));

                
//  A FileStream is needed to read the XML document.   
                 using  (Stream reader  =   new  FileStream(filename, FileMode.Open))
                {
                    
//  Declare an object variable of the type to be deserialized.   
                    config  =  (ReWriterConfig)serializer.Deserialize(reader);
                }
                HttpContext.Current.Cache.Insert(
" RewriterConfig " , config,  new  CacheDependency(filename));
            }
            
return  (ReWriterConfig)HttpContext.Current.Cache[ " RewriterConfig " ];     
        }
    }
}

  我们仍然需要原URLRewriter类库中的ReWriterUtils类中的方法,不过对其中RewriteUrl方法进行了一点小的改动,增加了一个isRedirect参数,用来决定是执行Context.RewritePath()方法还是Context.Response.Redirect()方法,下面是源代码。

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Web;

namespace  URLRewriterTest
{
    
public   class  ReWriterUtils
    {
        
///   <summary>
        
///  Rewrite's a URL using  <b> HttpContext.RewriteUrl() </b> .
        
///   </summary>
        
///   <param name="context"> The HttpContext object to rewrite the URL to. </param>
        
///   <param name="isRedirect"> Redirect or rewrite path. </param>
        
///   <param name="sendToUrl"> The URL to rewrite to. </param>
         public   static   void  RewriteUrl(HttpContext context,  string  sendToUrl,  bool  isRedirect)
        {
            
string  x, y;
            RewriteUrl(context, sendToUrl, isRedirect, 
out  x,  out  y);
        }

        
///   <summary>
        
///  Rewrite's a URL using  <b> HttpContext.RewriteUrl() </b> .
        
///   </summary>
        
///   <param name="context"> The HttpContext object to rewrite the URL to. </param>
        
///   <param name="sendToUrl"> The URL to rewrite to. </param>
        
///   <param name="isRedirect"> Redirect or rewrite path. </param>
        
///   <param name="sendToUrlLessQString"> Returns the value of sendToUrl stripped of the querystring. </param>
        
///   <param name="filePath"> Returns the physical file path to the requested page. </param>
         public   static   void  RewriteUrl(HttpContext context,  string  sendToUrl,  bool  isRedirect,  out   string  sendToUrlLessQString,  out   string  filePath)
        {
            
//  see if we need to add any extra querystring information
             if  (context.Request.QueryString.Count  >   0 )
            {
                
if  (sendToUrl.IndexOf( ' ? ' !=   - 1 )
                    sendToUrl 
+=   " & "   +  context.Request.QueryString.ToString();
                
else
                    sendToUrl 
+=   " ? "   +  context.Request.QueryString.ToString();
            }

            
//  first strip the querystring, if any
             string  queryString  =  String.Empty;
            sendToUrlLessQString 
=  sendToUrl;
            
if  (sendToUrl.IndexOf( ' ? ' >   0 )
            {
                sendToUrlLessQString 
=  sendToUrl.Substring( 0 , sendToUrl.IndexOf( ' ? ' ));
                queryString 
=  sendToUrl.Substring(sendToUrl.IndexOf( ' ? ' +   1 );
            }

            
//  grab the file's physical path
            filePath  =   string .Empty;
            filePath 
=  context.Server.MapPath(sendToUrlLessQString);

            
if  (isRedirect)
            {
                
//  redirect the path
                context.Response.Redirect( " ~/ "   +  sendToUrlLessQString);
            }
            
else
            {
                
//  rewrite the path
                context.RewritePath( " ~/ "   +  sendToUrlLessQString, String.Empty, queryString);
            }

            
//  NOTE!  The above RewritePath() overload is only supported in the .NET Framework 1.1
            
//  If you are using .NET Framework 1.0, use the below form instead:
            
//  context.RewritePath(sendToUrl);
        }

        
///   <summary>
        
///  Converts a URL into one that is usable on the requesting client.
        
///   </summary>
        
///   <remarks> Converts ~ to the requesting application path.  Mimics the behavior of the 
        
///   <b> Control.ResolveUrl() </b>  method, which is often used by control developers. </remarks>
        
///   <param name="appPath"> The application path. </param>
        
///   <param name="url"> The URL, which might contain ~. </param>
        
///   <returns> A resolved URL.  If the input parameter  <b> url </b>  contains ~, it is replaced with the
        
///  value of the  <b> appPath </b>  parameter. </returns>
         public   static   string  ResolveUrl( string  appPath,  string  url)
        {
            
if  (url.Length  ==   0   ||  url[ 0 !=   ' ~ ' )
                
return  url;         //  there is no ~ in the first character position, just return the url
             else
            {
                
if  (url.Length  ==   1 )
                    
return  appPath;   //  there is just the ~ in the URL, return the appPath
                 if  (url[ 1 ==   ' / '   ||  url[ 1 ==   ' // ' )
                {
                    
//  url looks like ~/ or ~/
                     if  (appPath.Length  >   1 )
                        
return  appPath  +   " / "   +  url.Substring( 2 );
                    
else
                        
return   " / "   +  url.Substring( 2 );
                }
                
else
                {
                    
//  url looks like ~something
                     if  (appPath.Length  >   1 )
                        
return  appPath  +   " / "   +  url.Substring( 1 );
                    
else
                        
return  appPath  +  url.Substring( 1 );
                }
            }
        }
    }
}

  最后就是编写Global中的Application_BeginRequest事件了,在原有URLRewriter的基础上稍作修改。

protected   void  Application_BeginRequest( object  sender, EventArgs e)
{
    
string  requestedPath  =  Request.RawUrl.ToString();

    
//  get the configuration rules
     string  filename  =  Context.Server.MapPath( " . " +   " //ReWriterRules.xml " ;
    ReWriterConfig rules 
=  ReWriterConfiguration.GetConfig(filename);

    
//  iterate through each rule
     for  ( int  i  =   0 ; i  <  rules.Rules.Length; i ++ )
    {
        
//  get the pattern to look for, and Resolve the Url (convert ~ into the appropriate directory)
         string  lookFor  =   " ^ "   +  ReWriterUtils.ResolveUrl(Context.Request.ApplicationPath, rules.Rules[i].LookFor)  +   " $ " ;

        
//  Create a regex (note that IgnoreCase is set)
        Regex re  =   new  Regex(lookFor, RegexOptions.IgnoreCase);

        
//  See if a match is found
         if  (re.IsMatch(requestedPath))
        {
            
//  match found - do any replacement needed
             string  sendToUrl  =  ReWriterUtils.ResolveUrl(Context.Request.ApplicationPath, re.Replace(requestedPath, rules.Rules[i].SendTo));

            
//  Rewrite or redirect the URL
            ReWriterUtils.RewriteUrl(Context, sendToUrl, rules.Rules[i].IsRedirect);
            
break ;         //  exit the for loop
        }
    }
}

  好了,大功告成!使用上面的映射表,当你输入http://localhost/Default_performance.aspx时访问正常,事实上Default_后面可以添加任何字符,这些字符都将作为Default.aspx页面tab参数的值。同时,当你输入http://localhost/Default.aspx?tab=performance时页面会自动跳转到前面一个url,tab参数的值将被作为url的一部分。

URLRewriterTest.rar下载

Tag标签: URLRewriter
1
0
(请您对文章做出评价)
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值