自定义Route类
对应的扩展方法
使用自定义Route类
Route 是双向的
Route 的基本概念在此不多说,这里重点强调一下 ASP.NET MVC 中 Route 是双向的,这可以从 RouteBase 类的定义可以看出:
public abstract class RouteBase{ public abstract RouteData GetRouteData(HttpContextBase httpContext); public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values); }
GetRouteData 用于解析 Url,GetVirtualPath 用于生成 Url。
自定义Route类
在开始编写自定义的 Route 类之前,先定义一个将用在自定义的Route中的用于处理字符串的静态类:StringElegantHelper 类。StringElegantHelper 中有两个方法 Elegant 和 DeElegant,将分别用在 GetVirtualPath 和 GetRouteData 方法中,用于生成和解析优雅的 Url。
internal static class StringElegantHelper { public static readonly char minus = '-'; public static string Elegant(string s) { var builder = new StringBuilder(); var index = 0; foreach (var c in s) { if (c >= 'A' && c <= 'Z') { if (index > 0) builder.Append(minus); builder.Append(char.ToLower(c)); } else if (c == minus) { builder.Append(minus); builder.Append(minus); } else builder.Append(c); index++; } return builder.ToString(); } public static string DeElegant(string s) { var builder = new StringBuilder(); var iterator = s.GetEnumerator(); while (iterator.MoveNext()) { if (iterator.Current != minus) { builder.Append(iterator.Current); continue; } if (!iterator.MoveNext()) { builder.Append(minus); break; } if (iterator.Current == minus) builder.Append(minus); else builder.Append(iterator.Current); } return builder.ToString(); } }
StringElegantHelper 中有两个方法 Elegant 和 DeElegant,也可以使用正则表达式来实现,但效率不如上面这种最朴实的方式高。
有了 StringElegantHelper 类,写出 ElegantRoute 就简单多了,ElegantRoute 类的代码为:
public class ElegantRoute : Route { public static readonly string[] ToElegant = new [] { "controller", "action" }; public ElegantRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler) { } public override RouteData GetRouteData(HttpContextBase httpContext) { var result = base.GetRouteData(httpContext); if (result == null) return null; foreach (var key in ToElegant) HandleItem(result.Values, key, StringElegantHelper.DeElegant); return result; } public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { var elegantValues = new RouteValueDictionary(values); foreach (var key in ToElegant) HandleItem(elegantValues, key, StringElegantHelper.Elegant); return base.GetVirtualPath(requestContext, elegantValues); } private void HandleItem(RouteValueDictionary dict, string key, Func<string, string> handler) { if (!dict.ContainsKey(key)) return; // var value = dict[key]; if (!(value is string)) return; // dict[key] = handler(value as string); } }
注意,上面代码中只对 controller 和 action 的路由值进行了“优雅”处理,其它值并没有,为什么呢?
大家可以考虑以下网址:~/customers/details/ANTON
全部处理的话会变成:~/customers/details/a-n-t-o-n
代码其他部分比较简单,不多说了。
对应的扩展方法
实现自定义Route类后,还需要几个扩展方法,以方便在 global.asax 文件中使用:代码其他部分比较简单,核心代码为:
public static class ElegantRouteExtensions { public static ElegantRoute MapElegantRoute(this RouteCollection routes, string name, string url, object defaults) { var route = new ElegantRoute(url, new RouteValueDictionary(defaults), new RouteValueDictionary(), //constraints new RouteValueDictionary(), //dataTokens new MvcRouteHandler()); routes.Add(name, route); return route; } }
使用自定义Route类
借助上面的扩展方法,使用就很相当简单了。global.asax 文件中,把 routes.MapRoute 替换为 routes.MapElegantRoute 即可:
routes.MapElegantRoute( // MapRoute "Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional } );