ASP.NET4.0中我们使用Routing可以很轻松的将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("Customer详细页
"); 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("Customer详细页
"); 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("Customer详细页
"); Response.Write(string.Format("CustomerID : {0}", id)); } } }
运行,没问题,而且也不存在在URL上再次附加?Id=1的问题了。
Asp.Net 4.0 SEO增强之 UrlRouting
1. Url Routing 的通常用法
UrlRouting在Asp.Net Mvc项目中被广泛使用,在Mvc中很好用,所以移植到了webform中,我们先看下在webform中的使用方式
假定一个使用场景:我们需要做博客每日文章的页面,我们希望的url地址是:
/archive/2010/05/10/default.aspx
这个地址将被映射到~/posts.aspx文件上
要使用UrlRouting,需要将UrlRouting的规则注册到RouteTable中,如下Global文件中注册Routing规则的代码
public static void RegisterRoutes(RouteCollection routes) { routes.Ignore("{resource}.axd/{*pathInfo}"); routes.MapPageRoute("blogs", //给这个UrlRouting规则起一个名字 "archive/{year}/{month}/{date}/default.aspx", //希望的友好Url地址格式 "~/blogs.aspx", //映射到的aspx页面路径 false, //是否需要检查用户权限 new RouteValueDictionary{ { "year", DateTime.Now.Year }, { "month", DateTime.Now.Month }, {"date", DateTime.Now.Date} },//参数的默认值 new RouteValueDictionary { {"year",@"(19|20)d{2}"}, {"month",@"d{1,2}"}, {"date",@"d{1,2}"} } //参数的规则,我们在这里限制url中的年月日是我们想要的数据格式 ); } void Application_Start(object sender, EventArgs e) { //在Application_Start时注册的Routing规则 RegisterRoutes(RouteTable.Routes); }
2. 在页面中使用UrlRouting参数值
1) 在后台代码中使用Route的值
protected void Page_Load(object sender, EventArgs e) { string year = (string)RouteData.Values["year"]; string month = (string)RouteData.Values["month"]; string date = (string)RouteData.Values["date"]; }
2) 在页面上使用
-
-
3) 在DataSource中使用RouteParameter
4) 在页面上显示RouteUrl
'>2010年5月1日的博客
3. UrlRouting和UrlRewrite的区别
UrlRouting相对于Url重写是一个比较新的事物,UrlRouting的长处是可以做双向转换,既可以做url重写,还可以根据一些参数获得重写后的Url地址,但是它也有自己的不足之处,比如说如果你想连域名一起重写,比如博客地址yukaizhao.cnblogs.com这样的重写,UrlRouting就做不到了,只能用UrlRewrite。