Url Rewrite 再說Url 重寫

前幾天看到園子裏一篇關於 Url 重寫的文章《獲取ISAPI_Rewrite重寫後的URL》 , URL-Rewrite 這項技術早已不是一項新技術了,這個話題也已經被不少人討論過屢次。搜索一下URL-Rewrite能夠找到不少URL-Rewrite方面的文章和組件,本身之前也屢次接觸過這個東東,也來講說吧。 ScottGu 有一篇很是經典的 URL-Rewrite Blog 
Tip/Trick: Url Rewriting with ASP.NET http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspxhtml


爲何要進行URL-Rewrite 
ScottGu的blog中給出了兩個重要的緣由: 
1.保證WebApplication在進行結構調整,移動頁面位置時,用戶收藏的URL不會所以而成爲死鏈。 
2. SEO優化。 
摘引自ScottGu Blog 的原文 
--------------------------------------------------------------------------- 
Why does URL mapping and rewriting matter? 
The most common scenarios where developers want greater flexibility with URLs are: 
1) Handling cases where you want to restructure the pages within your web application, and you want to ensure that people who have bookmarked old URLs dont break when you move pages around. Url-rewriting enables you to transparently forward requests to the new page location without breaking browsers. 
2) Improving the search relevancy of pages on your site with search engines like Google, Yahoo and Live. Specifically, URL Rewriting can often make it easier to embed common keywords into the URLs of the pages on your sites, which can often increase the chance of someone clicking your link. Moving from using querystring arguments to instead use fully qualified URLs can also in some cases increase your priority in search engine results. Using techniques that force referring links to use the same case and URL entrypoint (for example: weblogs.asp.net/scottgu instead of weblogs.asp.net/scottgu/default.aspx) can also avoid diluting your pagerank across multiple URLs, and increase your search results. 
In a world where search engines increasingly drive traffic to sites, extracting any little improvement in your page ranking can yield very good ROI to your business. Increasingly this is driving developers to use URL-Rewriting and other SEO (search engine optimization) techniques to optimize sites (note that SEO is a fast moving space, and the recommendations for increasing your search relevancy evolve monthly). For a list of some good search engine optimization suggestions, Id recommend reading the SSW Rules to Better Google Rankings, as well as MarketPositions article on how URLs can affect top search engine ranking. 
--------------------------------------------------------------------------- 
  第一點緣由中所描述的場景,在Web站點改版中常常碰到。Web站點改版常常會調整一些頁面的位置,QueryString中參數的結構等等。極可能使原來用戶在收藏夾中收藏的連接成爲死鏈。在這種場景下URL-Rewrite像是軟件架構技術中的一箇中間層的概念,URL-Rewrite對外公開的URL是被重寫過的,這個URL被用戶收藏,不會變,當Web站點調整,內部Page的位置改變了,使得內部實際的URL地址也改變了,這時修改內部的重寫規則,讓原來對外公開的URL重寫到新的內部URL上。從而保證了對外URL不變,其實對內已經完成了頁面位置的調整。雖然URL-Rewrite能夠作到防止死鏈的產生,可是大多數站點在改版或調整時,不會使用URL-Rewrite來防止死鏈的產生,通常會直接修改404 The page cannot be found 頁面,把404出錯頁面改爲一個更加友好的提示頁面,而且會在幾秒鐘以後跳轉到網站首頁。linux


  第二點緣由是SEO了,若是您的站點是個內部OA ERP CRM這種站點,只須要本身內部人員來訪問。其實徹底能夠不用作SEO,由於這種站點根本不須要搜索引擎來收錄,也不須要別人經過搜索引擎找到這個站點,因此這種站點徹底沒有必要進行SEO優化。若是您的站點是個商業站點,新聞站點,娛樂站點,越多人訪問越好的站點,SEO優化是很是重要,此時經過URL-Rewrite進行SEO優化也就很是必要了。隨着搜索引擎逐漸成爲人們查找信息,索取資源的首選工具,搜索引擎對一個站點的影響也就越來越大,下面是 zhangsichu.com 9-1~9-10 這段時間內的第三方來路數據統計。ios


11_142504_74v0urlRewrite


來路統計是經過記錄httpheader中的Referer,來得知用戶在瀏覽這個頁面以前所在的那個頁面。從而得出用戶是經過那個頁面到達這個頁面的。 
在266個獨立IP中,有200個IP是來自搜索引擎的。也就是說,用戶先經過搜索引擎的搜索結果,而後來到zhangsichu.com的用戶有200個。佔到了75.2%。一大半的人是經過搜索來的。充分說明了SEO對站點的重要,在這種狀況下,就必須作URL-Rewrite進行SEO優化了。web

  
若是您的站點既不須要考慮URL兼容防止死鏈問題,也不須要進行SEO優化,就徹底沒有必要進行URL-Rewrite。URL-Rewrite是一個對性能有害的處理過程。正則表達式


經常使用的URL-Rewrite方案 
URL-Rewrite既能夠發生在Web服務器(IIS/Apache)一級,也能夠發生在Web應用程序一級(Asp.Net/Jsp/PHP/…)。編程

  
1.Web應用程序級別的URL-Rewrite 
  在Web應用程序級別的URL-Rewrite。有三個比較著名的現成組件。 
  1) 微軟提供的 URL-Rewrite http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx 
  2) Open Source的 UrlRewriter.NET http://urlrewriter.net/ 
  3) UrlRewriting http://www.urlrewriting.net/en/Download.aspxapi


這種組件內部核心的工做原理: 都是在本身的Web Application的web.config中添加httpModule。用這個httpModule來處理重寫。(其實也可繼承System.Web.HttpApplication,在Application_BeginRequest中插入一個本身的方法處理重寫)瀏覽器


其中核心的處理代碼,下面的代碼摘引自UrlRewriter.NET組件。 
  1)從IHttpModule繼承獲得一個本身的HttpModule,這個HttpModule須要在web.config中配置,說明全部的請求都要通過這個HttpModule。服務器

public  sealed  class  RewriterHttpModule : IHttpModule
   {
     /// <summary>
     /// Initialises the module.
     /// </summary>
     /// <param name="context">The application context.</param>
     void  IHttpModule.Init(HttpApplication context)
     {
       context.BeginRequest += new  EventHandler(BeginRequest);
     }
private  void  BeginRequest( object  sender, EventArgs e)
     {
       // Add our PoweredBy header
       HttpContext.Current.Response.AddHeader(Constants.HeaderXPoweredBy, Configuration.XPoweredBy);
 
       _rewriter.Rewrite();
     }
}

 

2)讀取重寫規則,判斷是否須要重寫,肯定如何重寫,進行重寫。cookie

 

public  void  Rewrite()
     {
       string  originalUrl = ContextFacade.GetRawUrl().Replace( "+" , " " );
       RawUrl = originalUrl;
 
       // Create the context
       RewriteContext context = new  RewriteContext( this , originalUrl,
         ContextFacade.GetHttpMethod(), ContextFacade.MapPath,
         ContextFacade.GetServerVariables(), ContextFacade.GetHeaders(), ContextFacade.GetCookies());
 
       // Process each rule.
       ProcessRules(context);
 
       // Append any headers defined.
       AppendHeaders(context);
 
       // Append any cookies defined.
       AppendCookies(context);
 
       // Rewrite the path if the location has changed.
       ContextFacade.SetStatusCode(( int )context.StatusCode);
       if  ((context.Location != originalUrl) && (( int )context.StatusCode < 400))
       {
         if  (( int )context.StatusCode < 300)
         {
           // Successful status if less than 300
           _configuration.Logger.Info(MessageProvider.FormatString(Message.RewritingXtoY,
             ContextFacade.GetRawUrl(), context.Location));
 
           // Verify that the url exists on this server.
           HandleDefaultDocument(context); // VerifyResultExists(context);
 
           ContextFacade.RewritePath(context.Location);
         }
         else
         {
           // Redirection
           _configuration.Logger.Info(MessageProvider.FormatString(Message.RedirectingXtoY,
             ContextFacade.GetRawUrl(), context.Location));
 
           ContextFacade.SetRedirectLocation(context.Location);
         }
       }
       else  if  (( int )context.StatusCode >= 400)
       {
         HandleError(context);
       }
       else  if  (HandleDefaultDocument(context))
       {
         ContextFacade.RewritePath(context.Location);
       }
 
       // Sets the context items.
       SetContextItems(context);
     }

 

這種重寫是ASP.NET Pipeline級別的重寫,能夠重寫一切Asp.net接管的請求。

 

11_144458_zwo3urlRewrite1

 

在這裏對/Pd/Book.aspx的請求被重寫到了 /Pd.aspx?Cg=books. 
Web應用程序級別的URL-Rewrite只能重寫Web應用程序接管的請求。它沒有辦法處理.js .jpg的重寫。緣由是這些請求到達IIS後,IIS根本就沒有把這些請求分發到Asp.Net,因此這些請求就不會發生重寫的處理和操做。在IIS中能夠配置,對哪些後綴的請求是被IIS分發到Asp.Net的。

 

11_142520_fc5iurlRewrite2

 

若是您必定要在Asp.Net級別對.js的請求進行重寫,能夠在這裏指定.js的請求由Asp.Net接管,可是這時您須要本身處理.js的Response。Web服務器級別的URL-Rewrite能夠比較好的解決這方面的問題吧。


2. Web服務器級別的URL-Rewrite

 


Apache服務器 
Apache服務器原生支持了URL-Rewrite。在config中打開LoadModule rewrite_module modules/mod_rewrite.so 而後配置重寫的正則表達式。例如:


摘引自Apache2.2中文參考手冊 中文手冊 Apache-UrlRewrite

---------------------------------------------
描述:
這個規則的目的是強制使用特定的主機名以代替其餘名字。好比,你想強制使用www.example.com代替example.com,就能夠在如下方案的基礎上進行修改:
解決方案:
對運行在非80端口的站點
 
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{SERVER_PORT} !^80$
RewriteRule ^/(.*) http: //fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R]
 
對運行在80端口的站點
 
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/(.*) http: //fully.qualified.domain.name/$1 [L,R]
---------------------------------------------------------------------------

 

IIS6/IIS7 Web服務器 
IIS7新的「管道模式」實際上是把ASP.NET中的某些概念與IIS進行了更加深度的集成。在IIS7 Program Manager: Mike Volodarsky的Blog中有一篇文章分析了這方面的內容: 
Breaking Changes for ASP.NET 2.0 applications running in Integrated mode on IIS 7.0

 


IIS7的「經典模式」與IIS 6基本上是一模一樣的。


在IIS6 + Asp.Net應用程序級的URL-Rewrite,只能在請求被分配到Asp.Net引擎後才能發生重寫操做。在IIS7這一點被改變了。IIS7能夠對沒有後綴名的請求進行重寫,Asp.Net和IIS7進行了深度的集成。IIS7能夠在 IIS 請求管道的任何地方執行一個HttpModule,下面是一個IIS7下Asp.Net的重寫配置:


摘引自ScottGu的Blog

 

<?xml version= "1.0"  encoding= "UTF-8" ?>
 
<configuration>
 
<configSections>
<section name= "rewriter"
requirePermission= "false"
type= "Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter"  />
</configSections>
 
<system.web>
 
<httpModules>
<add name= "UrlRewriter"  type= "Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter"  />
</httpModules>
 
   </system.web>
 
<system.webServer>
 
<modules runAllManagedModulesForAllRequests= "true" >
<add name= "UrlRewriter"  type= "Intelligencia.UrlRewriter.RewriterHttpModule"  />
</modules>
 
<validation validateIntegratedModeConfiguration= "false"  />
 
</system.webServer>
 
<rewriter>
<rewrite url= "~/products/(.+)"  to= "~/products.aspx?category=$1"  />
</rewriter>
 
</configuration>

 

其中:<rewrite url="~/products/(.+)" to="~/products.aspx?category=$1" />這條規則中的~/products/(.+)這條正則表達式。匹配了/products/下的全部連接。 
IIS6服務器級別下的重寫須要使用ISAPI Filters Rewrite來實現。


ISAPI Filters有兩個很是著名工程: 
  1)Helicon Techs ISAPI Rewrite: http://www.isapirewrite.com/ 提供一個99美圓(可免費試用30天)的ISAPI URL重寫產品完整版,以及一個免費的輕量級版本。 
  2)Ionics ISAPI Rewrite: http://cheeso.members.winisp.net/IIRF.aspx 全免費開源組件。 
  在 ISAPI Filter編程重寫URL 中有說明。


服務器級的重寫與應用程序級的重寫最大的區別在於他們發生的時機不一樣。下圖是在服務器級把/Pd/Book.aspx重寫到/Pd.aspx?Cg=books

 

 

11_142526_52t8urlRewrite3

 

請求尚未到Asp.Net引擎,就被重寫了。


3.Asp.Net級別上重寫的一些小細節問題(部份內容源自ScottGu 的Blog) 
  若是頁面中存在form且form是runat=server的<form runat="server">,那麼這個頁面在重寫後form的action是原始URL,不是重寫後乾淨的URL。例如/Pd/Book.aspx重寫到/Pd.aspx?Cg=books這個場景。實際用戶瀏覽器訪問的地址是/Pd/Book.aspx,在服務器級被重寫後請求變成了/Pd.aspx?Cg=books,在這種狀況下form的action會被render成/Pd.aspx?Cg=books,其實這時更加想要action被render成/Pd/Book.aspx,讓頁面PostBack到同一位置。在某些狀況下action被render成/Pd.aspx?Cg=books是不會對正常工做有影響的,只要/Pd.aspx?Cg=books不被重寫規則匹配上,/Pd.aspx?Cg=books會被正確發回到Asp.Net引擎。可是瀏覽器上的地址欄會變化,暴露出真正的地址。若是這個URL被某個別的規則匹配,那就必需要求form的action被正確的Render成/Pd/Book.aspx,這種統一的重寫後的URL。


解決辦法: 
  1)本身包裝form控件。把url寫在某個hidden field裏同postback一塊兒回來,render時修改action爲hidden field裏的url. 
  2)使用JavaScript在form submit前修改action例如 window.document.forms[0].action = window.location; 
  3)使用ASP.NET 2.0 Control Adapter(源自ScottGu 的Blog) 
  這種重寫是當在使用Asp.Net應用程序一級的重寫時,使用Context.Request.RawUrl填寫form的action,當使用IIS應用服務器一級的重寫時把乾淨的URL記錄在Request.ServerVariables["HTTP_X_REWRITE_URL"]中,使用Request.ServerVariables["HTTP_X_REWRITE_URL"]填寫form的action,填寫form action 的過程都是經過Control Adapter對form Control擴展,override form控件的 WriteAttribute方法,在Render時從新指定form的action。


核心源代碼 摘引自ScottGu的Blog

11_142533_pmesurlRewrite4

 

重寫後路徑兼容問題 
  在/Pd/Book.aspx重寫到/Pd.aspx?Cg=books的場景中,頁面中若是有相對位置的資源,如某個img的src=」../logo.gif」或src=」logo.gif」。這時瀏覽器請求這些資源基準的位置是/pd/也就是說src=」../logo.gif」請求的路徑是/logo.gif,src=」logo.gif」請求的路徑是/pd/logo.gif。可是其實這些資源的基準位置是 / 由於原始的URL是/Pd.aspx?Cg=books。這時就會發生資源找不到的狀況。 
  1)使用服務器端的img使用 ~ 路徑能夠解決這個問題(源自ScottGu的Blog)。 
  2)使用<base href=" http://xxx/ "></base>標籤,這個標籤須要寫在head裏。告訴頁面,頁面中全部相對路徑的基準路徑是http://xxx/ ,從而解決重寫後路徑失效的問題。 
  base標籤的說明: http://www.w3school.com.cn/tags/tag_base.asp

  到這裏,URL-Rewrite的問題討論完了。在實際項目中確定還會遇到各類不一樣的問題,不過解決的思路,估計會是上面這些技術的組合和擴展,但願經過上面對URL-Rewrite問題的討論,會對遇到的新問題能起到一些幫助的做用。

相關文章
相關標籤/搜索