ASP.NET. Several Optional Parameters in body of Urls 1

Routing in the last versions of ASP.NET makes a great progress. Compare to earlier versions of ASP.NET, new Routing features are magical. Especially in ASP.NET MVC, It completely separates the routing from Controllers and makes the routing completely loosely coupled from the functional codes of Controllers

But there are still some deficiencies. One of them that annoys me in several projects, is having multiple optional parameters in the body of a Url. Something like the following route.

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

As can be seen, there are two optional parameters. There is no issue in case all parameters have values. The problem arises when param1 is null, but param2 has a value. What I expect in this situation is "{controller}/{action}/{param2}", but what the routing infrastructure offers is "{controller}/{action}/{param1}". The problem is, the RoutingModule can not detect that the provided route value is param2 not param1.

Such scenario can be happened in implementing Advance Search pages where users can select several search items and all search items must be accessible by Urls. Consider the following model and controller.

 public class TestModel
    {
        public string Country { get; set; }
        public string Maker { get; set; }
        public string Type { get; set; }
    }

   public class TestController : Controller
    {
        public ActionResult Index(TestModel model)
        {
            return View(model);
        }
    }

The simplest solution is using querystring like follows to address the different calls of the action, with a simple route.

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

and the urls look like the following

http://[DomainName]/Test/Index?Country=austria&Maker=bmw
http://[DomainName]/Test/Index?Maker=bmw&Type=Luxury

but such Urls are really ugly. It is very nice, if we could embed the first 3 or 4 main parameters in the Urls like the following Route and Urls.

routes.MapRoute(
                name: "Test",
                url: "Test/{Country}/{Maker}/{Type}",
                defaults: new { controller = "Test", action = "Index", Country = UrlParameter.Optional, Maker = UrlParameter.Optional, Type = UrlParameter.Optional },
                lookupParameters: new string[] { "Country", "Maker", "Type" },
                routeValueService: new RouteValueService());

http://[DomainName]/Test/austria/bmw
http://[DomainName]/Test/bmw/truck

Why the default routing provider of ASP.NET can not cover that condition. The issue is, ASP.NET Routing can not detect that in the second url, there is no Country and it maps the route as follows:

http://[DomainName]/Test/bmw/red {Country=bmw, Maker=Luxury}

The solution that I developed is simple. With the assumption that there is no conflict among the different values of parameters, I pass a service to the routing mechanism that is used by the RoutingModule to map the route values correctly. Something like this:

    routes.MapRoute(
                name: "Test",
                url: "Test/{Country}/{Maker}/{Type}",
                defaults: new { controller = "Test", action = "Index", Country = UrlParameter.Optional, Maker = UrlParameter.Optional, Type = UrlParameter.Optional },
                lookupParameters: new string[] { "Country", "Maker", "Type" },
                lookupService: new LookupService());

and the LookupService is:

   public class LookupService : IRouteValueLookupService
    {
        public string GetRouteTypeName(string routeValue)
        {
            switch (routeValue)
            {
                case "Austria":
                case "Germany":
                case "England":
                    return "Country";
                case "BMW":
                case "Audi":
                case "Fiat":
                    return "Maker";
                case "Luxury":
                case "Hatchback":
                case "Truck":
                    return "Type";
            }

            return string.Empty;
        }
    }

The lookupParameters arguments specifies the route parameters that the routing mechanism must query the lookupService to map the values to them. The lookupService gets a route value and return its parameter name. Now, we can have the following urls:

http://[DomainName]/Test/austria/bmw
http://[DomainName]/Test/bmw/truck

The implementation involves few extensions and extended classes. But using it is very simple. The only thing that you need to do is referencing the ICP.CustomRouting assembly and then you can define your custom routes.

You can download the source code here. In the next post I explain the implementations.


No Comments

Post Reply