UPDATE: This applies to MVC 5 too
I was kind of surprised recently when I wanted to create an asp.net MVC view with dashes in it and found out that I couldn’t.
To me, reading-a-url-like-this is better than readingaurllikethis so I thought there must be a way to get around this. After all, web apps like WordPress and Umbraco allow this so why can’t I just do it in asp.net MVC?
After doing a bit of research on the subject I found out that I needed to do a couple of things:
- Create a new class that extends the MvcRouteHandler class within my Global.asax file, and
- Create a route within my Route.Config file.
Extending the MvcRouteHandler
The first thing to note here is that the underscore character _ is perfectly acceptable within controller names and also within action names. So with that in mind any urls you want to write out with dashes in them should have underscores within them within your controller or actions names like so:
public class Laptops_and_TabletsController : Controller { public ActionResult Budget_Laptops() { return View(); } }
The next step is to extend the MvcRouteHandler class to replace those underscores with dashes so that when the above action is requested it will be written out like /laptops-and-tablets/budget-laptops. So, open up your Global.asax file and add the following code block in:
public class HyphenatedRouteHandler : MvcRouteHandler { protected override IHttpHandler GetHttpHandler(RequestContext requestContext) { requestContext.RouteData.Values["controller"] = requestContext.RouteData.Values["controller"].ToString().Replace("-", "_"); requestContext.RouteData.Values["action"] = requestContext.RouteData.Values["action"].ToString().Replace("-", "_"); return base.GetHttpHandler(requestContext); } }
Next it’s onto the Route.Config file.
Route.Config
The changes we need to make to this file are minimal as all we’re going to do is replace the default route handler with our new one:
Replace this
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
With this
routes.Add( new Route("{controller}/{action}/{id}", new RouteValueDictionary( new { controller = "Home", action = "Index", id = "" }), new HyphenatedRouteHandler()) );
And that’s it. You’ll now be able to provide urls that read-like-this over urls that readlikethis.
Nice post Craig. I’ve created an open source project on GitHub and also a NuGet package for this. You are welcome to contribute it if you want to improve asp.net mvc url issues: https://github.com/AtaS/lowercase-dashed-route
Thanks Ata. I’ll definitely take a look at your GitHub project.
What about Areas?
Pingback: Journey to ASP .NET MVC 5 (Episode 2) | cuteprogramming
This is a cool solution to the problem however it does raise a number of concerns.
This seems to allow for any page to be hit from any number of URLs.
E.g.,
/user/info can be hit from:
/us-er/inf–o
/user—-/—–inf—–o
etc.
While in and of itself this would rarely manifest as a security risk it will unfortunately decrease your pagerank with many search engines (repeating the same content across a bunch of pages is seen as an attempt to increase the association of certain keywords with your site in general I believe).
For intranet sites this is rarely a concern but if you want to do something similar in a production consumer site I believe using attribute routing is the superior choice.
Please ignore my comment. I misread the code.
No problem Barak. Thanks for reading and contributing.
I did everything as your guideline..but when i run in url its shows me url with “_” it not replace it with “-” while loading.
i have to to this manually in url… as i replaced in url and check..it works…Why it not replaced it remotely while loading ??
This was very useful for me in diagnosing an interesting 404 error I ran across. It let me know that a least a piece of the MVC pipeline was functioning as expected.
Great work!
Glad it helped you out. Thanks for commenting!