为了给用户更好的体验,我们一般会保持网站的url一致,一般url均为小写,更加美观。
这里以MVC为例:
1. 在MVC 5.0 中,有一个属性,非常简单好用,我们直接在 RouteConfig 的静态方法 RegisterRoutes 中设置 routes.LowercaseUrls = true;即可。
该属性用于 “ 获取或设置指示标准化虚拟路径时是否将 URL 转换为小写的值”。
2. 安装 LowsercaseRoutesMVC 组件,将RegisterRoutes方法中 routes.MapRoute 替换为 routes.MapRouteLowercase 即可。
3. 自己封装 Route来进行Url的匹配和解析。
MVC中,路由数据是封装在 RouteData 中的,RouteData 结构如下代码所示
1 public class RouteData 2 { 4 //确定路由是否匹配请求时,传递到路由处理程序但未使用的自定义值的集合。 5 public RouteValueDictionary DataTokens { get; } 6 7 //获取或设置表示路由的对象。 8 public RouteBase Route { get; set; } 9 10 //获取或设置处理所请求路由的对象。(MVC框架中自定义的HttpHandler(IRouteHandler提供 GetHttpHandler方法)便从此处获得) 11 public IRouteHandler RouteHandler { get; set; } 13 14 //获取路由的 URL 参数值和默认值的集合。 15 public RouteValueDictionary Values { get; } 17 18 // 使用指定标识符检索值。 19 public string GetRequiredString(string valueName); 20 }
RouteData中,类型为RouteBase 的Route属性,表示生成路由数据对应的路由对象。RouteBase是一个抽象类,结构如下:
//返回有关请求路由信息 public abstract RouteData GetRouteData(HttpContextBase httpContext); //重写时,检查路由,匹配则生成一个 URL,然后检索有关该路由的信息 public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
GetRouteData 用来解析Url,GetVirutalPath用来生成url. 常用的如:Url.Action,ActionLink、RouteLink、RedirectToAction 和 RedirectToRouter 内部都会调用 GetVirtualPath 方法来生成 Url。因为我们入手点便在重写的 GetVirutalPath 方法上。
MVC 中 RouteBase 的具体实现类是 Route,我们经常在 Global.asax 中经常使用:
public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); } //... }
MapRoute 返回 Route,我们可以写一个继承自 Route 的类,然后重写它的 GetVirtualPath 方法:直接贴代码
public class LowerCaseUrlRoute : Route {
/// <summary>
/// 需要小写的键值
/// </summary>
private static readonly string[] requiredKeys = new [] {"area","culture", "controller", "action" };
public LowerCaseUrlRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { } public LowerCaseUrlRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) : base(url, defaults, routeHandler){ } public LowerCaseUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) : base(url, defaults, constraints, routeHandler) { }
/// <summary>
/// Initializes a new instance of the <see cref="LowerCaseUrlRoute"/> class.
/// </summary>
/// <param name="url">路由的 URL 模式。</param>
/// <param name="defaults">要在 URL 不包含所有参数时使用的值。</param>
/// <param name="constraints">一个用于指定 URL 参数的有效值的正则表达式。</param>
/// <param name="dataTokens">传递到路由处理程序但未用于确定该路由是否匹配特定 URL 模式的自定义值。 这些值会传递到路由处理程序,以便用于处理请求。</param>
/// <param name="routeHandler">处理路由请求的对象。</param>
public LowerCaseUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler) { }
//重写GetVirtualPath以生成小写Url public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { LowerRouteValues(requestContext.RouteData.Values); LowerRouteValues(values); LowerRouteValues(Defaults); return base.GetVirtualPath(requestContext, values); }
//转换要小写的键值 private void LowerRouteValues(RouteValueDictionary values) { foreach (var key in requiredKeys) { if (values.ContainsKey(key) == false) continue; var value = values[key]; if (value == null) continue; var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); if (valueString == null) continue; values[key] = valueString.ToLower(); } var otherKyes = values.Keys .Except(requiredKeys, StringComparer.InvariantCultureIgnoreCase) .ToArray(); foreach (var key in otherKyes) { var value = values[key]; values.Remove(key); values.Add(key.ToLower(), value); } } }
自定义 LowerCaseUrlRoute 后,我们从网上直接Down一个 LowerCaseUrlRouter的扩展方法:代码如下
public static class LowerCaseUrlRouteExtensions { /// <summary> /// LowerCaseUrlRouter的扩展方法 /// </summary> /// <param name="routes"></param> /// <param name="name"></param> /// <param name="url"></param> /// <param name="defaults"></param> /// <returns></returns> public static LowerCaseUrlRoute MapUrl(this RouteCollection routes, string name, string url, object defaults) { var route = new LowerCaseUrlRoute(url, new RouteValueDictionary(defaults), new RouteValueDictionary(), //constraints new RouteValueDictionary(), //dataTokens new MvcRouteHandler()); //获取当前项目控制器的Namespace,使其默认应用在这个Namespace下 var type = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType; if (type != null) { var namespaces = new[] { type.Namespace + ".Controllers" }; route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; } public static LowerCaseUrlRoute MapUrl(this RouteCollection routes, string name, string url, object defaults, string[] namespaces) { var route = new LowerCaseUrlRoute(url, new RouteValueDictionary(defaults), new RouteValueDictionary(), //constraints new RouteValueDictionary(), //dataTokens new MvcRouteHandler()); if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; } public static LowerCaseUrlRoute MapUrl(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { var route = new LowerCaseUrlRoute(url, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(), new MvcRouteHandler()); if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; } }
routes.MapUrl( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
修改RouteConfig中如上图所示,大功搞成!