用路由 Route 優化頁面地址
剛看到某人發的關于 Route 的文章,就把我今天下午發到團隊的一封郵件轉發過吧。
全文如下,有修改:
優化地址無非就兩個選擇?URLRewrite?和?MVC?里的路由(Route)
關于?URLRewrtie?和?Route?的區別,可參考:
http://www.infoq.com/cn/news/2008/11/urlrewriting
從?.NET 3.5 SP1?起,?微軟把?MVC?路由單獨抽出來,放到?System.Web.Routing?下, WebForm?程序從此可以用上路由了.
.NET 4?對路由做了改進,?使用起來很簡單..?我們的項目都是?.NET 4?的, SEO?以后肯定是要做的,?所以我試著對?Hotel.Online?做了一下路由.
現在把使用過程中的注意事項說一下.
1, Route?使URL?的層次目錄改變了,?原來是?HotelInfo.aspx ,?路由后,很可能是?HotelInfo/45956/2011-06-21?這一小小的改變,卻帶來了大的影響.
A, PostBack :?原來的?Form action = “HotelInfo.aspx” ,?路由后變成?action=”2011-06-21”?了, PostBack?當然是不對的.?要避免這個問題,請在代碼里加上這句:
this.Form.Action?=?Page.ResolveUrl("~/HotelInfo.aspx");
B, script?標簽的問題?:?在頁面里?link?標簽的地址(href)會自動轉換,?但是?script?標簽的地址(src)卻不會自動轉換,?解決這個問題,有兩個辦法
l??A, base , <base href=”/xxx” />?如?<base href=”/xxx” /><script src=”main.js”></script>?就會自動指向?/xxx/main.js ,?這個需要注意先后順序,?如果?script?出現在?base?前,則沒有這個效果.
l??使用絕對地址.?為了簡化,?和?ResourceSite?的處理方式一樣,?我在?BasePage?里將?~?符號替換成了? http://xxx.xxx.xxxx/xxx ?,?所以,?只要繼承?BasePage?的頁面,?都可以直接寫?<script src=”~/main.js”></script>
C, A標簽的問題,?我看HTML代碼里有很多?<a href=”###” onclick=”…”?的寫法,?在不加?base?之前, ###?指向當前頁,?但是加了?base?后, ###?就指向?base?設定的地址了.為了避免這種情況,?應使用?href=”javascript:void()” onclick=”…”?或?href=”javascript:doSomething();return false;”?或干脆就不用?A?標簽.
2,?使用Route?后訪問地址從?HotelInfo.aspx?hid=45956&ds=2011-06-21& de=2011-06-25?變成了?HotelInfo/45956/2011-06-21/2011-06-25 ,?使用?Request.QueryString取不出?hid , ds, de?這些URL?參數了,?因為?hid, ds, de?就不是以?url?參數形式出現的.?要獲取這些值,?就要使用另外一個東西: Page.RouteData.Values[key]?了,?這個?key不區分大小寫,和?QueryString?一樣.
3,?腳本里的?POST / GET ,?原來的頁在都在同一個目錄層次,?所以直接
var?form?=?document.createElement("FORM");
form.action?=?"HotelList.aspx";
是沒有問題的,?能找到?HotelList.aspx?這個地址,?但是現在目錄層次改變了,?在這樣寫就會找不到地址,?就如第一點的?A?里所描述的.?要解決這個問題,?要使用?base?或絕對地址.
以上是我在對?Hotel.Online?做路由遇到的問題.
下面說說使用.
1, WebApplication?或?網站要是?.NET Framework 4 (也可以用?3.5 ,?但是具體有什么不同,我沒有去了解,?下面的示例代碼是針對?4?的)
2,?引用?System.Web.Routing?這個命名空間.
3,?在網站啟動的時候,注冊路由.?一般是放到?Global?的?Application_Start?里. .NET 4?里,允許用另外的方法: PreApplicationStartMethod 。
4,?注冊路由是通過?RouteTable.Routes. MapPageRoute?或?Add?方法. MapPageRoute?是對?Add?方法的簡化操作.
我的實現:
RouteItemBase.cs
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Web.Routing;
namespace?XXX.Frameworks.Route?{
????///?<summary>
????///?
????///?</summary>
????public?abstract?class?RouteItemBase?{
????????///?<summary>
????????///?路由名稱,必須唯一
????????///?</summary>
????????public?abstract?string?RouteName?{
????????????get;
????????}
????????///?<summary>
????????///?路由地址
????????///?</summary>
????????public?abstract?string?RouteUrl?{
????????????get;
????????}
????????///?<summary>
????????///?物理地址
????????///?</summary>
????????public?abstract?string?PhyicalUrl?{
????????????get;
????????}
????????///?<summary>
????????///?路由的默認值
????????///?</summary>
????????public?abstract?RouteValueDictionary?Default?{
????????????get;
????????}
????????///?<summary>
????????///?約束
????????///?</summary>
????????public?abstract?RouteValueDictionary?Constraint?{
????????????get;
????????}
????????///?<summary>
????????///?
????????///?</summary>
????????///?<param?name="routes"></param>
????????///?<param?name="item"></param>
????????public?static?void?Map(RouteCollection?routes,?RouteItemBase?item)?{
????????????routes.MapPageRoute(item.RouteName?,?item.RouteUrl,?item.PhyicalUrl,?false,?item.Default,?item.Constraint);
????????}
????????///?<summary>
????????///?
????????///?</summary>
????????///?<returns></returns>
????????public?abstract?object?Format();
????}
}
HotelInfoRoute
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Web.Routing;
using?XXX.Frameworks.Const;
using?XXX.Frameworks.Route;
namespace?Hotel.Online.Route?{
????///?<summary>
????///?
????///?</summary>
????public?class?HotelInfoRoute?:?RouteItemBase?{
????????public?object?Hid?{
????????????get;
????????????set;
????????}
????????public?DateTime?Ds?{
????????????get;
????????????set;
????????}
????????public?DateTime?De?{
????????????get;
????????????set;
????????}
?
?
????????#region?RouteItemBase
????????public?override?string?RouteName?{
????????????get?{
????????????????return?RouteNames.HotelInfo.ToString();
????????????}
????????}
?
????????public?override?string?RouteUrl?{
????????????get?{
????????????????return?"HotelInfo/{hid}/{ds}/{de}";
????????????}
????????}
????????public?override?string?PhyicalUrl?{
????????????get?{
????????????????return?"~/HotelInfo.aspx";
????????????}
????????}
????????public?override?RouteValueDictionary?Default?{
????????????get?{
????????????????return?new?RouteValueDictionary(new?{
????????????????????Ds?=?DateTime.Now.AddDays(1).ToString(DateFormat.Date),
????????????????????De?=?DateTime.Now.AddDays(2).ToString(DateFormat.Date)
????????????????});
????????????}
????????}
????????public?override?RouteValueDictionary?Constraint?{
????????????get?{
????????????????return?new?RouteValueDictionary(new?{
????????????????????Hid?=?@"\d+",
????????????????????Ds?=?@"\d{4}-\d{2}-\d{2}",
????????????????????De?=?@"\d{4}-\d{2}-\d{2}"
????????????????});
????????????}
????????}
????????public?override?object?Format()?{
????????????return?new?{
????????????????Hid?=?this.Hid,
????????????????Ds?=?this.Ds.ToString(DateFormat.Date),
????????????????De?=?this.De.ToString(DateFormat.Date)
????????????};
????????}
????????#endregion
????}
}
注意?HotelInfoRoute?的?RouteUrl ,?HotelInfo/{hid}/{ds}/{de}?Hid, ds ,de?即路由的參數,?不區分大小寫.
為了編程方便(?強類型?),?我同時在這個類里定義了?Hid, Ds , De?幾個屬性( 注意: 和路由參數完全一樣,? 不區分大小寫 ),?這幾個屬性會在頁面獲取?Route Url?和獲取路由參數里發揮作用,會面會說.
Global?里
????????void?Map(RouteCollection?routes)?{
????????????routes.RouteExistingFiles?=?true;
????????????routes.Ignore("{resource}.axd/{*pathInfo}");
????????????routes.Ignore("{*AllRes}",?new?{
????????????????AllRes?=?@".*?\.(?!aspx)(.*)"
????????????});
????????????
????????????//RouteItemBase.Map(routes,?new?HotelInfoRoute());
????????????RouteHelper.AutoLoadRoutes(routes,?typeof(HotelInfoRoute).Assembly);
????????}
????????void?Application_Start(object?sender,?EventArgs?e)?{
????????????Map(RouteTable.Routes);
….
….
RouteHelper.AutoLoadRoutes?是用來自動發現某個?Assembly?下所有繼承自?RouteItemBase?的類,?并自動注冊到當前的路由表中,?這樣,就不用每加個路由,都要手動修改?Global了.
頁面里
var?routeData?=?this.Page.RouteData.Values;
var HotelID?=?(routeData["hid"]????"").ToString().ToInt(-110),
這樣寫,有些隱患,?假如路由參數?{hid}?變成了?{hotelID}?了,?而你在頁面里還在傻傻的用?hid ,?當然是取不到值的,?怎么辦呢??用強類型可以很好的避免這個問題.?怎么用?
Page.RouteData.Values 是一個 RouteValueDictionary 類型, 繼承自 IDictionary<string,?object> , 我針對 IDctionary<string, object> 做了一個擴展方法.
?
????????public?static?T?ToEntity<T>(this?IDictionary<string,?object>?dict)?where?T?:?new()?{
????????????var?t?=?typeof(T);
????????????var?ps?=?t.GetProperties();
????????????T?tmp?=?new?T();
????????????foreach?(var?p?in?ps)?{
????????????????if?(!p.CanWrite)
????????????????????continue;
????????????????//var?v?=?dict.Get(p.Name);
????????????????if?(dict.ContainsKey(p.Name))?{
????????????????????p.SetValue(tmp,?Convert.ChangeType(dict[p.Name],?p.PropertyType),?null);
????????????????}
????????????}
????????????return?tmp;
????????}
?
然后直接用:
this.RouteData?=?this.Page.RouteData.Values.ToEntity<HotelInfoRoute>(); 就把該頁面的路由參數提取出來了
var HotelID 直接等于 RouteData.Hid 就是了.
?
獲取路由后的地址, 被我簡化成如下了,
????????public?static?string?GetRoutedUrl<T>(T?routeData)
????????????where?T?:?RouteItemBase?{
????????????return?RouteTable.Routes.GetVirtualPath(HttpContextHelper.Current.Request.RequestContext,?routeData.RouteName.ToString(),?new?RouteValueDictionary(
????????????????routeData.Format()
????????????????)).VirtualPath;
????????}
?
HttpContextHelper 的定義在 XXX.Frameworks.Extends.HttpContextHelper 中定義, 為什么不用 HttpContext? 因為單元測試的時候會有問題.
????????????return?RouteHelper.GetRoutedUrl<HotelInfoRoute>(new?HotelInfoRoute()?{
????????????????Hid?=?hotelID.ToString(),
????????????????Ds?=?this.BeginDate,
????????????????De?=?this.EndDate
????????????});
?
還是強類型, 不怕改.
?
?
?
?
將以有地址重定向到新地址
HotelInfo.aspx 這個地址肯定以經被搜索引擎收錄, 現在如果棄用這樣的地址, 對SEO和以有用戶來說, 是最不希望出現的.
如果把老地址301 到新地址,對SEO和用戶不會有任何影響. 為了達到這個效果,我加了一個方法:
?
????????public?static?void?RedirectToRouted(this?Page?page,?string?routeName)?{
????????????if?(page.RouteData.Route?==?null)?{
????????????????var?context?=?HttpContextHelper.Current;
????????????????var?querys?=?context.Request.QueryString;
????????????????var?keys?=?querys.Cast<string>().ToList();
????????????????RouteValueDictionary?datas?=?new?RouteValueDictionary();
????????????????keys.ForEach((k)?=>?{
????????????????????datas.Add(k,?querys[k]);
????????????????});
????????????????var?vp?=?RouteTable.Routes.GetVirtualPath(context.Request.RequestContext,?routeName,?new?RouteValueDictionary(datas));
????????????????context.Response.Status?=?"301?Moved?Permanently";
????????????????context.Response.RedirectLocation?=?vp.VirtualPath;
????????????????context.Response.End();
????????????}
????????}
?
在需要將老地址轉成新地址的頁面里,加上:
????????protected?override?void?OnPreInit(EventArgs?e)?{
????????????this.Page.RedirectToRouted(?RouteNames.HotelInfo.ToString()?);
????????????base.OnPreInit(e);
????????}
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
