扩展URL Routing:处理URL中的日期参数
对URL中的日期参数,老赵提供了一个非常经典的解决方案。但是这里的日期参数是以yyyy-MM-dd的形式出现的,用户往往会有这样的需求,即URL中的参数一定要为Demo/Date/2009/06/05的形式。由于RouteData是以/来划分segment的,老赵的方案似乎就不适用了。
国外的大牛们大多推崇将日期分隔为年、月、日,通过扩展IRouteConstraint来对年月日分别进行判断。引用老赵的话来说,“无论从易用性还是通用性等角度来看,这种做法都是下下之策。说实话,这样的做法其实并没有跳出框架既有功能给定的圈子,它只是通过“迎合框架”来满足自己的需求,而不是让框架为我们的需求服务。”
其实要实现这样的需求也不难,只需要对老赵的方案稍微修改一下就可以了。我们将URL定义为Demo/Date/{year}/{month}/{day},然后在生成RouteData时,将year、month、day组合成date;生成路径时,再将date划分为year、month、day。
新建一个DateFormatRoute类,让它继承自RouteBase。在GetRouteData方法中拼接年月日,其余方法与老赵的相同。
public override RouteData GetRouteData(HttpContextBase httpContext) { var routeData = route.GetRouteData(httpContext); if (routeData.Values["year"] != null) { string date = routeData.Values["year"].ToString() + routeData.Values["month"].ToString() + routeData.Values["day"].ToString(); routeData.Values.Remove("year"); routeData.Values.Remove("month"); routeData.Values.Remove("day"); routeData.Values["date"] = date; } else { return null; } var valuesModified = new Dictionary<string, object>(); foreach (var pair in routeData.Values) { var key = pair.Key; IRouteFormatter formatter = null; if (this.formatters.TryGetValue(key, out formatter)) { object o; if (formatter.TryParse(pair.Value, out o)) { valuesModified[key] = o; } else { return null; } } } foreach (var pair in valuesModified) { routeData.Values[pair.Key] = pair.Value; } return routeData; }
在GetVirtualPath方法中,再将年月日分解到各个segment中。
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { var routeValues = new RouteValueDictionary(); foreach (var pair in values) { var key = pair.Key; IRouteFormatter formatter = null; if (this.formatters.TryGetValue(key, out formatter)) { string path; if (formatter.TryToString(pair.Value, out path)) { routeValues[key] = path; } else { return null; } } else { routeValues[key] = pair.Value; } } if (routeValues["date"] != null) { string s = routeValues["date"].ToString(); routeValues.Remove("date"); routeValues["year"] = s.Substring(0, 4); routeValues["month"] = s.Substring(4, 2); routeValues["day"] = s.Substring(6, 2); } return route.GetVirtualPath(requestContext, routeValues); }
最后,修改Global.asax
routes.Add( "Demo.Date", new DateFormatRoute( "{controller}/{action}/{year}/{month}/{day}", new RouteValueDictionary(), // defaults new Dictionary<string, IRouteFormatter> { {"controller", new RegexFormatter("Demo")}, {"action", new RegexFormatter("Date")}, {"date", new DateTimeFormatter("yyyyMMdd")} }, new RouteValueDictionary(), // constaints new RouteValueDictionary(), // data tokens new MvcRouteHandler()));
打开浏览器测试一下,URL和链接都显示正常。
PS:对于URL Routing的扩展,实在是想找一个别的例子来进行探讨。但稍微复杂一点的类型也不可能通过URL体现出来,而稍微简单一点的,用正则表达式就可以约束了。所以最后只要生硬的来修改老赵的示例了……
也可能是我的思路还没有打开,大家有没有什么比较好的思路?欢迎在这里讨论。