Делая URL-адрес в нижнем регистре. Любой простой или встроенный способ?

Есть ли простой/встроенный способ сделать URL в нижнем регистре для MVC3.

У меня есть код, который может сделать это, но выглядит что-то простое в реализации.

 routes.MapRouteLowercase(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "index", id = UrlParameter.Optional } // Parameter defaults
            );

public class LowercaseRoute : Route
    {
        public LowercaseRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler)
        {
        }

        public LowercaseRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
            : base(url, defaults, routeHandler)
        {
        }

        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
            : base(url, defaults, constraints, routeHandler)
        {
        }

        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
            : base(url, defaults, constraints, dataTokens, routeHandler)
        {
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            VirtualPathData path = base.GetVirtualPath(requestContext, values);

            if (path != null)
            {
                path.VirtualPath = path.VirtualPath.ToLowerInvariant();
            }

            return path;
        }
    }




public static class RouteCollectionExtensions
    {
        public static Route MapRouteLowercase(this RouteCollection routes, string name, string url)
        {
            return routes.MapRouteLowercase(name, url, null /* defaults */, (object)null /* constraints */);
        }

        public static Route MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults)
        {
            return routes.MapRouteLowercase(name, url, defaults, (object)null /* constraints */);
        }

        public static Route MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults, object constraints)
        {
            return routes.MapRouteLowercase(name, url, defaults, constraints, null /* namespaces */);
        }

        public static Route MapRouteLowercase(this RouteCollection routes, string name, string url, string[] namespaces)
        {
            return routes.MapRouteLowercase(name, url, null /* defaults */, null /* constraints */, namespaces);
        }

        public static Route MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults, string[] namespaces)
        {
            return routes.MapRouteLowercase(name, url, defaults, null /* constraints */, namespaces);
        }

        public static Route MapRouteLowercase(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 = new RouteValueDictionary(defaults),
                Constraints = new RouteValueDictionary(constraints),
                DataTokens = new RouteValueDictionary()
            };

            if ((namespaces != null) && (namespaces.Length > 0))
            {
                route.DataTokens["Namespaces"] = namespaces;
            }

            routes.Add(name, route);

            return route;
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url)
        {
            return context.MapRouteLowercase(name, url, (object)null /* defaults */);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults)
        {
            return context.MapRouteLowercase(name, url, defaults, (object)null /* constraints */);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults, object constraints)
        {
            return context.MapRouteLowercase(name, url, defaults, constraints, null /* namespaces */);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, string[] namespaces)
        {
            return context.MapRouteLowercase(name, url, (object)null /* defaults */, namespaces);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults, string[] namespaces)
        {
            return context.MapRouteLowercase(name, url, defaults, null /* constraints */, namespaces);
        }

        public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces)
        {
            if (namespaces == null && context.Namespaces != null)
            {
                namespaces = context.Namespaces.ToArray();
            }

            Route route = context.Routes.MapRouteLowercase(name, url, defaults, constraints, namespaces);
            route.DataTokens["area"] = context.AreaName;

            // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up
            // controllers belonging to other areas
            bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0);
            route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;

            return route;
        }
    }

Ответы

Ответ 1

Я только что заметил, что в .NET Framework 4.5 есть новое свойство. Прекрасно работает! RouteCollection.LowercaseUrls

Установите LowercaseUrls на true

public static void RegisterRoutes(RouteCollection routes)
{
    routes.LowercaseUrls = true;
    ...
}

Создать ссылку

@Html.ActionLink("Log in", "Login", "Account")

Это создаст удивительный, строчный url:)

<a href="/account/login">Log in</a>

Ответ 3

Если ваши причины для обеспечения строчной буквы - это просто SEO, то лучшим решением, которое я нашел, является использование IIS 7 URL Rewrite Module

У вас не только есть возможность принудительно вводить все url в строчные буквы, но также иметь доступ к правилам, которые позволяют вам удалять/добавлять конечные слэши, применять канонические домены и т.д.

Блог пользователя RuslanY имеет хороший набор для начала. Например, я использую следующие на всех моих сайтах:

<!-- http://ruslany.net/2009/04/10-url-rewriting-tips-and-tricks/ -->
<rule name="Convert to lower case" stopProcessing="true">
    <match url=".*[A-Z].*" ignoreCase="false" />
    <conditions>
        <add input="{REQUEST_METHOD}" matchType="Pattern" pattern="GET" ignoreCase="false" />
    </conditions>
    <action type="Redirect" url="{ToLower:{R:0}}" redirectType="Permanent" />
</rule>

Вы просто добавляете вышеуказанные строки в раздел вашего web.config.

Ответ 4

Да, мне пришлось реализовать нечто подобное выше. Это, по-видимому, единственный способ сделать это.

Я хотел бы добавить, что кроме того мы добавили 301 переадресацию, так что любой трафик, исходящий из /Upper -Case-Url, будет 301 перенаправлен на/upper-case-url.

Ответ 5

Не уверен, что не так с вашим кодом (моя немного отличается, но по сути та же концепция). Он очень прост в реализации, и он полностью повторно используется.

LowerCaseRouteHelper.cs

using System.Web.Routing;
using System.Web.Mvc;

namespace Utilities.Helpers
{
    public class LowercaseRouteHelper : System.Web.Routing.Route
    {
        public LowercaseRouteHelper(string url, IRouteHandler routeHandler) : base(url, routeHandler)
        {
        }
        public LowercaseRouteHelper(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) : base(url, defaults, routeHandler)
        {
        }
        public LowercaseRouteHelper(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) : base(url, defaults, constraints, routeHandler)
        {
        }
        public LowercaseRouteHelper(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler)
        {
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            VirtualPathData path = base.GetVirtualPath(requestContext, values);

            if (path != null) {
                path.VirtualPath = path.VirtualPath.ToLowerInvariant();
            }

            return path;
        }
    }

    public static class RouteCollectionExtensions
    {
        [System.Runtime.CompilerServices.Extension()]
        public static void MapRouteLowercase(RouteCollection routes, string name, string url, object defaults)
        {
            routes.MapRouteLowercase(name, url, defaults, null);
        }

        [System.Runtime.CompilerServices.Extension()]
        public static void MapRouteLowercase(RouteCollection routes, string name, string url, object defaults, object constraints)
        {
            if (routes == null) {
                throw new ArgumentNullException("routes");
            }

            if (url == null) {
                throw new ArgumentNullException("url");
            }

            object route = new LowercaseRouteHelper(url, new MvcRouteHandler()) {
                Defaults = new RouteValueDictionary(defaults),
                Constraints = new RouteValueDictionary(constraints)
            };

            if (String.IsNullOrEmpty(name)) {
                routes.Add(route);
            } else {
                routes.Add(name, route);
            }
        }
    }
}

глобальный

routes.MapRouteLowercase("Start", "", new {
    controller = "Home",
    action = "Index"
})

Мне это нравится, и великая вещь (как говорят комментарии) заключается в том, что она действительно помогает улучшить SEO.

Ответ 6

Лол, вы, ребята, слишком много делаете для себя. Просто добавьте это в свой global.asax application_beginrequest...

protected void Application_BeginRequest(Object sender, EventArgs e) {
string url = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath);
if (Regex.IsMatch(url, @"[A-Z]")) {
  PermanentRedirect(url.ToLower() + HttpContext.Current.Request.Url.Query);
}
private void PermanentRedirect(string url) {
  Response.Clear();
  Response.Status = "301 Moved Permanently";
  Response.AddHeader("Location", url);
  Response.End();
}