在启用了LowercaseUrls的路由参数中保留大小写

本文关键字:参数 保留 大小写 路由 启用 LowercaseUrls | 更新日期: 2023-09-27 18:03:46

我在我的MVC 4应用程序中使用routes.LowercaseUrls = true;,它工作得很好。然而,参数也会被小写,所以如果我有一个像

这样的路由
routes.MapRoute(
    name: "MyController",
    url: "foo/{hash}/{action}",
    defaults: new { controller = "MyController", action = "Details" }
);

使用

生成的链接
@Html.ActionLink("my link", "Details", new { hash=ViewBag.MyHash })

URL的{hash}部分也将小写,例如,如果ViewBag.MyHash = "aX3F5U",则生成的链接将是/foo/ax3f5u而不是/foo/aX3F5U

是否有办法强制MVC只小写的控制器和动作部分?

对于旧版本的MVC,要走的路似乎是实现Route的自定义子类,但我不知道如何/在哪里实例化它,因为路由构造函数的签名与MapRoute完全不同,我希望有一个更简单的方法。

在启用了LowercaseUrls的路由参数中保留大小写

我认为Route的自定义子类的解决方案将是一个足够好的和简单的,但同时有点丑:)

可以在RouteConfig.csRegisterRoute方法上添加CustomRoute。添加以下代码代替routes.MapRoute

var route = new CustomRoute(new Route(
  url: "{controller}/{action}/{id}",
  defaults: new RouteValueDictionary() { 
    { "controller", "Home" }, 
    { "action", "Index" }, 
    { "id", UrlParameter.Optional }
  },
  routeHandler: new MvcRouteHandler()
));
routes.Add(route);

特定CustomRoute的实现可能如下所示:

public class CustomRoute : RouteBase
{
  private readonly RouteBase route;
  public CustomRoute(RouteBase route)
  {
    this.route = route;
  }
  public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
  {
    values = new RouteValueDictionary(values.Select(v =>
    {
      return v.Key.Equals("action") || v.Key.Equals("controller")
        ? new KeyValuePair<String, Object>(v.Key, (v.Value as String).ToLower())
        : v;
    }).ToDictionary(v => v.Key, v => v.Value));
    return route.GetVirtualPath(requestContext, values);
  }
  public override RouteData GetRouteData(HttpContextBase httpContext)
  {
    return route.GetRouteData(httpContext);
  }
}

然而,这不是一个最佳的实现。一个完整的例子可以使用RouteCollection的扩展和自定义Route子扩展的组合,以使其尽可能接近原始的routes.MapRoute(...)语法:

LowercaseRoute类:

public class LowercaseRoute : Route
{
    public LowercaseRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { }
    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        values = new RouteValueDictionary(values.Select(v =>
        {
            return v.Key.Equals("action") || v.Key.Equals("controller")
              ? new KeyValuePair<String, Object>(v.Key, (v.Value as String).ToLower())
              : v;
        }).ToDictionary(v => v.Key, v => v.Value));
        return base.GetVirtualPath(requestContext, values);
    }
}

RouteCollectionExtensions类:

public static class RouteCollectionExtensions
{
    [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
    public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url)
    {
        return MapLowercaseRoute(routes, name, url, null /* defaults */, (object)null /* constraints */);
    }
    [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
    public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults)
    {
        return MapLowercaseRoute(routes, name, url, defaults, (object)null /* constraints */);
    }
    [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
    public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
    {
        return MapLowercaseRoute(routes, name, url, defaults, constraints, null /* namespaces */);
    }
    [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
    public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, string[] namespaces)
    {
        return MapLowercaseRoute(routes, name, url, null /* defaults */, null /* constraints */, namespaces);
    }
    [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
    public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces)
    {
        return MapLowercaseRoute(routes, name, url, defaults, null /* constraints */, namespaces);
    }
    [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
    public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
    {
        if (routes == null)
        {
            throw new ArgumentNullException("routes");
        }
        if (url == null)
        {
            throw new ArgumentNullException("url");
        }
        Route route = new LowercaseRoute(url, new MvcRouteHandler())
        {
            Defaults = CreateRouteValueDictionary(defaults),
            Constraints = CreateRouteValueDictionary(constraints),
            DataTokens = new RouteValueDictionary()
        };
        if ((namespaces != null) && (namespaces.Length > 0))
        {
            route.DataTokens["Namespaces"] = namespaces;
        }
        routes.Add(name, route);
        return route;
    }
    private static RouteValueDictionary CreateRouteValueDictionary(object values)
    {
        var dictionary = values as IDictionary<string, object>;
        if (dictionary != null)
        {
            return new RouteValueDictionary(dictionary);
        }
        return new RouteValueDictionary(values);
    }
}

你现在可以用MapLowercaseRoute代替MapRoute,所以

routes.MapRoute(
    name: "MyController",
    url: "foo/{hash}/{action}",
    defaults: new { controller = "MyController", action = "Details" }
);

简直变成了

routes.MapLowercaseRoute(
    name: "MyController",
    url: "foo/{hash}/{action}",
    defaults: new { controller = "MyController", action = "Details" }
);

显示期望的行为

有一个简单的方法,

public class MyRoute : Route
{
    public MyRoute(string url, object defaults): base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    { 
    }
    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        if (values["action"] != null)
            values["action"] = values["action"].ToString().ToLowerInvariant();
        if (values["controller"] != null)
            values["controller"] = values["controller"].ToString().ToLowerInvariant();
        return base.GetVirtualPath(requestContext, values);
    }
}
routes.Add("Default",new MyRoute("{controller}/{action}/{id}",
                new { controller = "Home", action = "Index", id = MyUrlParameter.Optional }));

如果您查看private RouteCollection.NormalizeVirtualPath方法,您将看到它只是使用virtualPath.ToLowerInvariant()。所以没有办法处理它。即使你创建了自己的路由,它也是小写的。

但是你能做的是在"#"符号之后添加散列,即"foo/{action}/#{hash}"。我还没试过了,但应该管用。请看NormalizeVirtualPath的实现。

就像1.2.3一样简单,

看这个例子

   routes.MapRouteLowercase( // changed from routes.MapRoute
       "Default",
       "{controller}/{action}/{id}",
       new { controller = "Home", action = "Index", id = UrlParameter.Optional }
   );

它很容易通过Nuget下载和安装,我使用它。 PM> Install-Package LowercaseRoutesMVC

http://lowercaseroutesmvc.codeplex.com/