Siegeon
Siegeon

Reputation: 610

Route segment parameter issues

I am running into a problem with my routes in MVC4.

I have a few actions that live outside of a specific product and many more that live within the user chosen product. In order to accommodate the actions I have mapped two routes

        context.MapRoute(
            "CMS_product",
            "CMS/{productId}/{controller}/{action}/{id}",
            new { controller = MVC.CMS.Home.Name, action = MVC.CMS.Home.ActionNames.Index, productId = default(Guid).ToString(), id = UrlParameter.Optional },
            new string[] { "Areas.CMS.Controllers" }
        );

        context.MapRoute(
            "CMS_default",
            "CMS/{controller}/{action}/{id}",
            new { controller = MVC.CMS.Home.Name, action = MVC.CMS.Home.ActionNames.Index, id = UrlParameter.Optional },
            new string[] { "Areas.CMS.Controllers" }
        );

So while this works in a generic since, none of my routes will match the default route any longer and instead of getting a URL like

~/CMS/Product/List

When operating outside of a product I get urls like this.

~/CMS/00000000-0000-0000-0000-000000000000/Product/List

Another note: I have tried to hard code the Prodcut/List in as a route, and have placed it before CMS_product in the hopes that it would match prior to the other url. I feel like I must be overlooking something simple.

Upvotes: 0

Views: 73

Answers (2)

Siegeon
Siegeon

Reputation: 610

For completeness, should anyone else run into a similar issue here is the solution.

       // used to match ~/CMS/00000000-0000-0000-0000-000000000000/Product/List 
       // prevents the GUID.Empty from showing when there is no product value
       // in the segment
       context.MapRoute(
            name: "CMS_nullproduct",
            url: "CMS/{controller}/{action}/{id}",
            defaults: new { controller = MVC.CMS.Home.Name, action = MVC.CMS.Home.ActionNames.Index, id = UrlParameter.Optional },
            constraints: new { productId = Guid.Empty.ToString() },
            namespaces: new string[] { "Areas.CMS.Controllers" }
        ); 

        // matches any route with a productId segment value of anything aside from
        // GUID.Empty
        context.MapRoute(
            name: "CMS_product",
            url: "CMS/{productId}/{controller}/{action}/{id}",
            defaults: new { controller = MVC.CMS.Home.Name, action = MVC.CMS.Home.ActionNames.Index, id = UrlParameter.Optional },
            constraints: new { productId = @"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$" },
            namespaces: new string[] { "Areas.CMS.Controllers" }
        );

        context.MapRoute(
            name: "CMS_default",
            url: "CMS/{controller}/{action}/{id}",
            defaults: new { controller = MVC.CMS.Home.Name, action = MVC.CMS.Home.ActionNames.Index, id = UrlParameter.Optional },
            namespaces: new string[] { "Areas.CMS.Controllers" }
        );

Upvotes: 1

Sławomir Rosiek
Sławomir Rosiek

Reputation: 4073

In my opinion you should remove default value for productId.

context.MapRoute(
        "CMS_product",
        "CMS/{productId}/{controller}/{action}/{id}",
        new { controller = MVC.CMS.Home.Name, action = MVC.CMS.Home.ActionNames.Index, id = UrlParameter.Optional },
        new string[] { "Areas.CMS.Controllers" }
    );

If you not provide productId you routing engine should match second route and generate ~/CMS/Product/List but if you provide productId it match first rule.

Additionaly you can write custom IRouteConstraint or use regex to limit productId values.

context.MapRoute(
        "CMS_product",
        "CMS/{productId}/{controller}/{action}/{id}",
        new { controller = MVC.CMS.Home.Name, action = MVC.CMS.Home.ActionNames.Index, id = UrlParameter.Optional },
        new { productId = @"^\d{8}\-\d{4}\-\d{4}\-\d{4}\-\d{12}$" }
        new string[] { "Areas.CMS.Controllers" }
    );

Upvotes: 0

Related Questions