asp.net-mvcroutessegmentsurlhelper

The urlHelper build wrong path.. What is a rison for this?


I have a route rule as:

routes.MapRoute("HotToursPage",
            "HotTours/{countryTo}/{resort}/{param1}/{param2}/{param3}/{param4}/{param5}",
            new
            {
                controller = "HotTours",
                action = "Index",
                countryTo = UrlParameter.Optional,
                resort = UrlParameter.Optional,
                param1 = UrlParameter.Optional,
                param2 = UrlParameter.Optional,
                param3 = UrlParameter.Optional,
                param4 = UrlParameter.Optional,
                param5 = UrlParameter.Optional
            }
        );

In the code I have:

var dictionary = new RouteValueDictionary();
        aaa.Add("countryTo", countryToInfo.Translit);
        aaa.Add("resort", resort);
        aaa.Add("param1", param1);
string url = urlHelper.Action("Index", "HotTours", dictionary);

If there are param5, param6 and other, then

url =/hottours/?countryTo=tailand&resort=bangkok&param1=price_from_50000,

but if i remove param5, param6 and other, then all ok:

url =/hottours/tailand/bangkok/price_from_50000

Why if segment count is less then 7, all ok? I need 9 segments, but urlHelper builds wrong url in this case.


Solution

  • When building URLs, you have to provide all of the route values that are in the URL pattern. There is one exception - when the last parameter is optional, you don't need to include it.

    Therefore, to consistently deal with segments that could be optional in a long URL pattern, you need more than one route. Each route can only have one UrlParameter.Optional and it must be the right-most segment.

    routes.MapRoute("HotToursPage3",
        "HotTours/{countryTo}/{resort}/{param1}/{param2}/{param3}/{param4}/{param5}",
        new
        {
            controller = "HotTours",
            action = "Index",
            param5 = UrlParameter.Optional
        }
    );
    
    routes.MapRoute("HotToursPage2",
        "HotTours/{countryTo}/{resort}/{param1}/{param2}/{param3}",
        new
        {
            controller = "HotTours",
            action = "Index",
            param3 = UrlParameter.Optional
        }
    );
    
    routes.MapRoute("HotToursPage1",
        "HotTours/{countryTo}/{resort}/{param1}",
        new
        {
            controller = "HotTours",
            action = "Index",
            param1 = UrlParameter.Optional
        }
    );
    

    NOTE: I am assuming here that your {countryTo} and {resort} parameters are required. It doesn't seem that sensible to make them optional. However, if I am mistaken, you need another route to deal with those 2 segments being optional or alternatively you should provide sensible default values for them. Generally speaking, if there are no sensible defaults for a value it should be required in the URL.

    Do note that you still can only make a segment optional if none of the segments to the right of it are provided. Therefore, this combination will work:

    var dictionary = new RouteValueDictionary();
    dictionary.Add("countryTo", "test1");
    dictionary.Add("resort", "test2");
    dictionary.Add("param1", "test3");
    var url = Url.Action("Index", "HotTours", dictionary);
    

    But this combination will still build a query string:

    var dictionary = new RouteValueDictionary();
    dictionary.Add("countryTo", "test1");
    dictionary.Add("resort", "test2");
    dictionary.Add("param1", "test3");
    dictionary.Add("param2", "test4");
    dictionary.Add("param5", "test5");
    var url = Url.Action("Index", "HotTours", dictionary);
    

    If you want all 5 of your params to be optional (and in any order), you should use query strings, rather than putting them into the path.

    routes.MapRoute("HotToursPage",
        "HotTours/{countryTo}/{resort}",
        new
        {
            controller = "HotTours",
            action = "Index"
        }
    );
    

    An alternative (that I don't recommend) would be to build up a series of routes that have identifier segments, which allows you to place the values in any order. See ASP.Net MVC Handling Segments with Route.