Search code examples
asp.net-mvc-routinglowercasehyphenationasp.net-mvc-4

Enforce Hyphens in .NET MVC 4.0 URL Structure


I'm looking specifically for a way to automatically hyphenate CamelCase actions and views. That is, I'm hoping I don't have to actually rename my views or add decorators to every ActionResult in the site.

So far, I've been using routes.MapRouteLowercase, as shown here. That works pretty well for the lowercase aspect of URL structure, but not hyphens. So I recently started playing with Canonicalize (install via NuGet), but it also doesn't have anything for hyphens yet.

I was trying...

routes.Canonicalize().NoWww().Pattern("([a-z0-9])([A-Z])", "$1-$2").Lowercase().NoTrailingSlash();

My regular expression definitely works the way I want it to as far as restructuring the URL properly, but those URLs aren't identified, of course. The file is still ChangePassword.cshtml, for example, so /account/change-password isn't going to point to that.

BTW, I'm still a bit rusty with .NET MVC. I haven't used it for a couple years and not since v2.0.


Solution

  • This might be a tad bit messy, but if you created a custom HttpHandler and RouteHandler then that should prevent you from having to rename all of your views and actions. Your handler could strip the hyphen from the requested action, which would change "change-password" to changepassword, rendering the ChangePassword action.

    The code is shortened for brevity, but the important bits are there.

    public void ProcessRequest(HttpContext context)
    {
        string controllerId = this.requestContext.RouteData.GetRequiredString("controller");
        string view = this.requestContext.RouteData.GetRequiredString("action");
    
        view = view.Replace("-", "");
        this.requestContext.RouteData.Values["action"] = view;
    
        IController controller = null;
        IControllerFactory factory = null;
    
        try
        {
            factory = ControllerBuilder.Current.GetControllerFactory();
            controller = factory.CreateController(this.requestContext, controllerId);
    
            if (controller != null)
            {
                controller.Execute(this.requestContext);
            }
        }
        finally
        {
            factory.ReleaseController(controller);
        }
    }
    

    I don't know if I implemented it the best way or not, that's just more or less taken from the first sample I came across. I tested the code myself so this does render the correct action/view and should do the trick.