lower case urls

Topics: General
Jan 7, 2012 at 10:08 PM

I might suggest orchard migrate to a lower case url format, so that urls do now show with mixed case. Such as /Tags, /Users etc, are kinda ugly with the uppercase first letter. nitpicky, but that's what I am ;)

Jan 9, 2012 at 6:13 AM
Edited Jan 9, 2012 at 2:12 PM

UPDATE, getting errors when I try to login as a result of the below code. Please don't use unless you have time to debug. I think if RouteDescriptor's Route property were of type Route rather than RouteBase it might work better.

A quick hack i just tried, and it seems to work. Add this to Orchard.MVC.Routes:

 

public class LowercasingRoute : RouteBase
    {
        private RouteBase _inner;

        public LowercasingRoute(RouteBase routeToWrap)
        {
            _inner = routeToWrap;
        }

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            return _inner.GetRouteData(httpContext);
        }

        public override VirtualPathData GetVirtualPath(RequestContext context, RouteValueDictionary values)
        {
            var result = _inner.GetVirtualPath(context, values);
            if (result != null && result.VirtualPath != null)
            {
                result.VirtualPath =
                    result.VirtualPath.ToLowerInvariant();
            }
            return result;
        }
    }

 

In RouteDescriptor.cs, replace the Route { get; set; } property with this:

 

        private LowercasingRoute _route;
        public RouteBase Route
        {

            get { return _route; }
            set
            {
                if (!(value is LowercasingRoute))
                {
                    _route = new LowercasingRoute(value);
                }
                else
                {
                    _route = value as LowercasingRoute;
                }

            }
        }

And that's all. Any url's generated from routes will be lower case (but not the querystring part, I believe).

Jan 9, 2012 at 5:19 PM
Edited Jan 9, 2012 at 5:19 PM

Ooh, dirty hack ;)

I agree it'd be nice to be consistent but this should probably be fixed in the core routes. Also, this could cause problems with Alias (then again, you could just use Alias to customise any paths to whatever you want anyway...)

Jan 9, 2012 at 5:28 PM

Ive also noticed that case is not corrected in the urls, ie, /Tags/... and /tags/... will both work fine. so external linkes to multiple cased urls could create duplicate content problems quite easily. this is actually pretty bad for seo.

I've dealt with this on other projects by adding the canonical url link inside the html so we don't have to redirect every variation of a url.

 

but regardless of this, we still really need to have the links generated with lower cases, or at least make it an option.

 

Jan 9, 2012 at 6:02 PM

Yeah, it's dirty. Was just trying to see if it would work without huge updates to all modules to use LowerCaseRoute instead of Route. The "LowercaseRoute" subclass is based on the solution a lot of people on stackoverflow are using to force lower case routes in ASP.NET MVC. Except theirs works on Route, note RouteBase. There is some info in Route that RouteBase is not exposing.  

Jan 9, 2012 at 7:43 PM

There's no reason it shouldn't technically work, maybe you need to lowercase the url in GetRouteData(...) as well, routes aren't guaranteed to be case-insensitive. Also, it should definitely be optional - there could even be cases where someone might specifically need a route to be case sensitive, as bad an idea as that would be!

Jan 9, 2012 at 8:13 PM

i agree, i mostly just mean have all the out-of-the-box links changed to lower case, ie /Tags, /Users etc... any that can be user defined may opt for mixed case. This would also alleviate any headaches with changing all urls to lower case, they'll just already be lower.

May 11, 2012 at 2:56 PM
Edited May 31, 2012 at 6:43 PM

Any updates on this? I'm facing the same issue. I've implemented TheMonarch's hack and it does seem to work but alas, a 404 error logging in.

I had hoped the IIS plugin "Url Rewrite" with the lowercase enforce rule would suffice, but it alone also causes problems logging in.

I could care less if the /User/Account/Login and beyond urls were not lowered especially with a rel no-follow in the link and blocking rule in robots.txt. So, is there a way to use TheMonarch's hack just for the urls generated on the front end of Orchard sites? Would we be so lucky to have a class in the route taxonomy that we could target that handles the urls generated in say the menu and also is tapped for such modules as google sitemap generators?

Update

It looks like the form tag on the login page is using the wrong path:

<form method="post" action="/settings/account/logon?returnurl=%2f">

It should be /users/account/logon?returnurl=%2f

Update

If you add area = "Orchard.Users" in the route values for the form tag on the LogOn.cshtml page in Orchard.Users module, login will work fine with TheMonarch's hack.

@using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new { area = "Orchard.Users", ReturnUrl = Request.QueryString["ReturnUrl"] }))) {

Update

Alright, this is ticking time bomb. Everything looked good until I tried to search for a module in the gallery tab in the modules section of the dashboard and alas, a 404 error. Again it's going to /settings instead of /packaging. I'm not sure what the deal here, but it's not cool.

Update

Okay, I think I have this under control now. Scratch everything, including TheMonarch's hack.

It's a 1-line change, add the following in Orchard.Mvc.Routes.ShellRoute GetVirtualPath, right before "return virtualPath":

            virtualPath.VirtualPath = virtualPath.VirtualPath.ToLowerInvariant();

Update

After giving this a good try, I wouldn't recommend fiddling with this. For one, pages like the Admin - Settings will be broken. The Settings page uses the Index action, like Settings/Index. And a lower-cased Index in the url path doesn't register for the Index action for some reason. And I'm confident this was the cause to many issues I've run into over the path couple weeks. Just Install the IIS Url Rewrite module and slap a good lower case rule for everything except the admin, packaging, modules, users and themes path:

        <rule name="LowerCaseRule1" stopProcessing="true">
          <match url="[A-Z]" ignoreCase="false" />
          <action type="Redirect" url="{ToLower:{URL}}" />
          <conditions>
                        <add input="{REQUEST_URI}" pattern="^/(admin|packaging|modules|users|themes)" negate="true" />
          </conditions>
        </rule>


Sep 28, 2012 at 6:24 PM

Update, was messing with this again (I never did use the hack I tried above). 

I added Levitikon's code to GetVirtualPath() in ShellRoute.cs: 

virtualPath.VirtualPath = virtualPath.VirtualPath.ToLowerInvariant();

And also fixed a bug in Orchard.Core/Settings/Routes.cs, SettingsActionConstraint, so that it matches based on case insensitive string comparisons. This fixes the problem Levitikon found with searching for modules in the dashboard, and also the problem where accessing /Admin/Settings/Index failed. 

    public class SettingsActionConstraint : IRouteConstraint {
        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
            if (routeDirection == RouteDirection.UrlGeneration)
                return true;

            if (!values.ContainsKey(parameterName))
                return false;
            
            // just hard-coding to know action name strings for now
            var potentialActionName = values[parameterName] as string;
            return !string.IsNullOrWhiteSpace(potentialActionName)
                && !potentialActionName.Equals("Index", StringComparison.InvariantCultureIgnoreCase)
                && !potentialActionName.Equals("Culture", StringComparison.InvariantCultureIgnoreCase)
                && !potentialActionName.Equals("AddCulture", StringComparison.InvariantCultureIgnoreCase)
                && !potentialActionName.Equals("DeleteCulture", StringComparison.InvariantCultureIgnoreCase);
        }
    }

I searched the entire source tree for other Route constraints that might be case sensitive but didn't find any. Custom modules may use things like custom route constraints, or regex constraints that are case sensitive. 

Would anyone be interested in making a general change to Orchard to support forcing all generated routes to be lowercase? We could make this a setting in the dashboard, and GetVirtualPath() in ShellRoute.cs would only convert to lowercase if the setting were enabled.