Dynamic routes with ASP.net MVC – Category/Location mapping to controller

D

I’ve recently been looking in to building a directory type application with Asp.Net MVC

One of the requirements would be to have SEO friendly URLs such as:

http://www.mysite.com/restaurants/Camberley—Surrey

/restaurants being the “Category”

/Camberley—Surrey being the “Location”

So, I created a ‘CategoryController’ like this

public class CategoryController : Controller
{
    public ActionResult Index(string category, string location)
    {
        //do stuff
        return View();
    }
}

But how do I map my routes?

What you can use, is RouteConstraints

Simply map your route as usual, but add an instance of our custom RouteConstraint

routes.MapRoute(
    name: "CategoryRoute",
    url: "{category}/{location}",
    defaults: new { controller = "Category", action = "Index", location = UrlParameter.Optional },
    constraints: new { category = new CategoryRouteConstraint() }
    );

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

With our CategoryRouteConstraint as follows:

public class CategoryRouteConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        //these would usually come from a database, or cache.
        var categories = new[] { "restaurants", "cafes", "bistros" };

        if (values[parameterName] == null)
            return false;

        //get the category passed in to the route
        var category = values[parameterName].ToString();

        //now we check our categories, and see if it exists
        return categories.Any(x => x == category.ToLower());

        // url such as /restaurants/Camberley--Surrey will match
        // url such as /pubs/Camberley--Surrey will not
    }
}

Of course, our collection of ‘categories’ would come from some kind of store – a database, cache, etc…

Now, if we run the app and visit /restaurants/Camberley–Surrey, our route resolves as we expect.

Demo: On github