0
votes

In my MVC application we have around 200+ pages where we are using grids. Recently we added history.js integration with grids which adds "page" parameter in the URL like http://localhost/User/ManageUsers/?page=1.

This is creating problem for breadcrumbs. If my URL is http://localhost/User/ManageUsers, it shows "Home > Manage Users > Users" in breadcrumbs. But if the URL is changed with page i.e. http://localhost/User/ManageUsers/?page=1, breadcrumb changes to parent i.e. "Home > Manage Users" where Manage Users doesn't have hyperlink.

My sitemap is designed as below:

  <mvcSiteMapNode title="Manage Users" controller="User" action="ManageUsers">
    <mvcSiteMapNode title="Users" controller="User" action="ManageUsers" preservedRouteParameters="id">
      <mvcSiteMapNode title="Facilities" controller="User" action="ManageUserFacilities" preservedRouteParameters="id,conf"/>
      <mvcSiteMapNode title="Conversations" controller="User" action="UserConversations" preservedRouteParameters="id,conf"/>
    </mvcSiteMapNode>
    <mvcSiteMapNode title="User Postings" controller="User" action="ManageUserPostings"/>
  </mvcSiteMapNode>

I have found if somehow I remove "/" from last in URL, breadcrumbs works fine e.g.

   http://localhost/User/ManageUsers?page=1 //shows correct breadcrumbs
   http://localhost/User/ManageUsers/?page=1 //shows parent node in breadcrumbs

Please help me finding the logical solution for this.

1
Please post your routing configuration.NightOwl888
routes.MapRoute( name: "DefaultwithType", url: "{controller}/{action}/{id}/{type}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, type = UrlParameter.Optional } ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); </code>sarojanand

1 Answers

0
votes

I can't explain (or reproduce) the behavior you are seeing with the trailing slash, but I do see a potential issue.

The following 2 nodes are ambiguous when it comes to matching the node against the incoming route:

<mvcSiteMapNode title="Manage Users" controller="User" action="ManageUsers">
  <mvcSiteMapNode title="Users" controller="User" action="ManageUsers" preservedRouteParameters="id">

In both cases, they will match on a URL /User/ManageUsers. This is because preservedRouteParameters only functions when there is a parameter to preserve, so it acts like an optional value.

The simplest way to fix this is to rename one of the action methods. Typically, these are 2 different pieces of functionality (named Index and Details) and applying separation of concerns by putting them into different action methods is a good thing.

Alternatively, you could define a separate route for the first node and then call out that route explicitly.

routes.MapRoute(
    name: "User_MangageUsers",
    url: "User/ManageUsers",
    defaults: new { controller = "User", action = "ManageUsers" }
);      

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

And your nodes:

<mvcSiteMapNode title="Manage Users" controller="User" action="ManageUsers" route="User_MangageUsers">
  <mvcSiteMapNode title="Users" controller="User" action="ManageUsers" preservedRouteParameters="id">

That really doesn't address the problem with the page parameter. But you shouldn't have one because query string values are ignored by default. I suspect there is a problem with your routing configuration, but since you didn't post it I can't tell you exactly what it is.

In v4.6.10 there was a feature added that allows you to use query string paramters as part of the route matching, and there was an enhancement to make the query string keys case-insensitive in v4.6.18.

If you are using a lower version than v4.6.10, you should upgrade. If you are using at least v4.6.10 you might be able to fix this by adding the page parameter as a preserved route parameter.

<mvcSiteMapNode title="Manage Users" controller="User" action="ManageUsers" route="User_MangageUsers" preservedRouteParameters="page">
  <mvcSiteMapNode title="Users" controller="User" action="ManageUsers" preservedRouteParameters="id">

Update Based on Your Routes

I am still unable to reproduce the problem.

However, you do have a problem with your route configuration - namely, it is not valid to specify 2 optional parameters on a single route. This might have something to do with your trailing slash issue because when you leave both optional parameters off, the result will include an extra trailing slash when the URL is generated. With your current route configuration, the Default route is an unreachable execution path.

You can fix this by changing your routing around.

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

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

Now:

/User/ManageUsers/1/SomeType - matches DefaultwithType
/User/ManageUsers/1          - matches DefaultwithType
/User/ManageUsers            - matches Default
/User                        - matches Default (with action "Index")
/                            - matches Default (with controller "Home", action "Index")

With this routing configuration, you can remove the ambiguity between the nodes by specifying the routes explicitly. This may or may not be necessary - try fixing your nodes first, and then this.

<mvcSiteMapNode title="Manage Users" controller="User" action="ManageUsers" route="Default">
  <mvcSiteMapNode title="Users" controller="User" action="ManageUsers" route="DefaultwithType" preservedRouteParameters="id">

Update to make Grid work on "Users" Node

// Route to force match with "Users" node
routes.MapRoute(
    name: "User",
    url: "User/ManageUsers/{id}",
    defaults: new { controller = "User", action = "ManageUsers" }); 

routes.MapRoute(
    name: "DefaultwithType",
    url: "{controller}/{action}/{id}/{type}"); 

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

Nodes:

<mvcSiteMapNode title="Manage Users" controller="User" action="ManageUsers" route="Default">
  <mvcSiteMapNode title="Users" controller="User" action="ManageUsers" route="User" preservedRouteParameters="id,page">

Now the User route will take precedence over the Default route. The DefaultwithType route won't match unless a 4 segment URL is supplied - you may need some fixed segments or constraints if you have URLs in your application that are 4 segments that you don't intend to call the last segment {type}.

I have also included the "page" in preservedRouteParameters. This probably isn't necessary, but if it is included it should match whether or not the page number is in the request.

"MvcSiteMapProvider_AttributesToIgnore" is for making custom attributes on the node that are not included in the route match. It has nothing to do with ignoring values in the incoming URL.