前言
大家都知道MVC里利用Routing的特性將地址映射到Controller和Action上,其實因為本身Routing是.Net 4.0內置的特性了,所以Web form上其實也可以適用的,今天我們就來看看如何做一下URL地址的優化,目的是將http://localhost/Customer.aspx?Id = 1優化成http://localhost/Custome/1的形式。
正文
首先,建立一個空的ASP.NET 4.0 Web form項目,建立Global.asax文件,在Glolal類里,我們添加如下代碼:
namespace EasyURL
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routeCollection)
{
//routeCollection.MapPageRoute("RouteForCustomer", "Customer/{Id}", "~/Customer.aspx")
// 可以使用上面的,但是最好使用下面的,這樣可以限制Id為數字
routeCollection.MapPageRoute("RouteForCustomer", "Customer/{Id}", "~/Customer.aspx", true, null, new RouteValueDictionary(new { Id = "\\d+" }));
}
}
}
然后添加一個Customer.aspx文件,代碼非常簡單,如下:
public partial class Customer : Page
{
protected void Page_Load(object sender, EventArgs e)
{
string id = Page.RouteData.Values["Id"].ToString();
Response.Write("<h1>Customer詳細頁</h1>");
Response.Write(string.Format("CustomerID : {0}", id));
}
}
注意我們是用Page.RouteData.Values["Id"]來取的值。
這樣,當我們在訪問http://localhost/customer/1的時候,就顯示了我們預想的頁面:
Customer詳細頁
CustomerID : 1
延伸一
其實上面的效果已經很簡單的實現了,但是我們發現我們必須要用Page.RouteData.Values["Id"]這種形式來取值,而不是我們平時所用的Page.QueryString["Id"]來取,那我們能否做到這一點呢?
我們知道,在PageLoad之前Request的值都有初始化好的,所以如果我們要使用這種方式的時候,必須在這個周期之前將RouteData.Values的值都加到QueryString里,好,我們來試試,先建立一個PageBase基類(后面所有的頁面都要繼承此類),代碼如下:
public abstract class PageBase : System.Web.UI.Page
{
protected override void OnInitComplete(EventArgs e)
{
base.OnInitComplete(e);
Page.RouteData.Values.Keys.ToList().ForEach(key =>
{
Request.QueryString.Add(key, Page.RouteData.Values[key].ToString());
});
}
}
運行之后,我們發現出了黃頁:
Exception Details: System.NotSupportedException: Collection is read-only.
Source Error:
Line 20: Page.RouteData.Values.Keys.ToList().ForEach(key =>
Line 21: {
Line 22: Request.QueryString.Add(key, Page.RouteData.Values[key].ToString());
Line 23: });
Line 24: } |
原來是QueryString這個集合是只讀的,通過老趙的文章一個較完整的關鍵字過濾解決方案我們來改寫一下代碼:
public static class GlobalValues
{
public static PropertyInfo NameObjectCollectionBaseIsReadOnly;
static GlobalValues()
{
Type type = typeof(NameObjectCollectionBase);
NameObjectCollectionBaseIsReadOnly = type.GetProperty(
"IsReadOnly",
BindingFlags.Instance | BindingFlags.NonPublic);
}
}
public abstract class PageBase : System.Web.UI.Page
{
protected override void OnInitComplete(EventArgs e)
{
base.OnInitComplete(e);
// 將集合改成可寫的
GlobalValues.NameObjectCollectionBaseIsReadOnly.SetValue(Request.QueryString, false, null);
Page.RouteData.Values.Keys.ToList().ForEach(key =>
{
// 添加RouteData.Values的name/value對添加到QueryString集合里
Request.QueryString.Add(key, Page.RouteData.Values[key].ToString());
});
}
}
然后使用我們平時用的代碼:
protected void Page_Load(object sender, EventArgs e)
{
//string id = Page.RouteData.Values["Id"].ToString();
string id = Request.QueryString["Id"];
Response.Write("<h1>Customer詳細頁</h1>");
Response.Write(string.Format("CustomerID : {0}", id));
}
運行一下,完美,啊哈!OK,在aspx里添加一個form表單,點擊一個button幾次試試,完蛋了,瀏覽器地址欄的地址變成了:
http://localhost/Customer/1?Id=1&Id=1&Id=1
原來是,由於我們每次都往QueryString里添加Id這個key,結果每次回發的時候就附加到URL上了,得想辦法在頁面結束以后從QueryString里刪除這個key,由於在OnUnload周期Request/Response已經被清空了,所以我們要在之前的周期內處理,代碼如下:
protected override void OnPreRenderComplete(EventArgs e)
{
Page.RouteData.Values.Keys.ToList().ForEach(key =>
{
Request.QueryString.Remove(key);
});
base.OnPreRenderComplete(e);
}
照例運行,OK,完美,再試幾下其它的case,也沒什么問題。
注:由於好久不用web form做項目了,所以不太確信上述代碼用的OnInitComplete/OnPreRenderComplete周期是否是合理的周期,目前運行起來是沒有問題的,有時間再詳細研究一下。
延伸二
延伸一的代碼,頗為復雜,而且也不知道有沒有副作用,其實實際項目中,一般我們都封裝自己的GetParameter方法,所以其實我們可以這樣用,相對就簡單多了:
namespace EasyURL
{
public abstract class PageBase : System.Web.UI.Page
{
public string GetQueryString(string key, string defaultValue = "")
{
if (Page.RouteData.Values.Keys.Contains(key))
return Page.RouteData.Values[key].ToString();
else if (string.IsNullOrWhiteSpace(Request.QueryString[key]))
return Request.QueryString[key];
else
return defaultValue;
}
}
public partial class Customer : PageBase
{
protected void Page_Load(object sender, EventArgs e)
{
string id = this.GetQueryString("id");
Response.Write("<h1>Customer詳細頁</h1>");
Response.Write(string.Format("CustomerID : {0}", id));
}
}
}
運行,沒問題,而且也不存在在URL上再次附加?Id=1的問題了。
同步與推薦
本文已同步至目錄索引:《大叔手記全集》
大叔手記:旨在記錄日常工作中的各種小技巧與資料(包括但不限於技術),如對你有用,請推薦一把,給大叔寫作的動力。