Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: ignoreRouteParameters #404

Closed
fcsobel opened this issue Jul 29, 2015 · 4 comments
Closed

Enhancement: ignoreRouteParameters #404

fcsobel opened this issue Jul 29, 2015 · 4 comments

Comments

@fcsobel
Copy link

fcsobel commented Jul 29, 2015

Would it be possible to add a node setting like ignoreRouteParameters (opposite of preserveRouteParametrs). To tell the matching code to ignore the listed parameters.

We want matching (but not preserving) for this scenario when ignoreRouteParameters="id"
image

We currently use preserveRouteParametrs="id" to get this to work for "/person/edit/{id}" and "/order/edit/{id}" routes but this has the side effect of adding "id" to the links in the sitemap for both order and person which we do not need or want.
/person/edit/123
/order/edit/123

@NightOwl888
Copy link
Collaborator

To what end?

Why are your routes including the ID if you don't want them generated there? You could make your routes URLs and they would ignore the ID when they are generated.

"/person/edit" 
"/order/edit"

More to the point, why do you need preservedRouteParameters="id" if "id" isn't even part of your URL?

@fcsobel
Copy link
Author

fcsobel commented Jul 30, 2015

Let me try a better example. We have routes to pages with optional parameters:

/orders/{id:int?}
/people/{id:int?}

We use sitemap to navigate to the pages and we never want to see ID persisted in the sitemap.

/orders
/people

We have a search UI that handles sending the user to the page with ID (nothing to do with sitemap)

/orders/1234
/people/5678

This simple structure worked under 3.x.

When we upgraded to 4.x we had to add preservedRouteParameters="id" to get /orders/1234 to match the correct node which forces IDs into the sitemap.

We are trying to get back to the 3.x behavior without persisting values into the sitemap.

Here is our work around to match without preservedRouteParameters="id". This works but seems wrong to inject values to get them to be ignored and we are concerned it could break something else.

public class MvcSiteMapIgnoreParameters : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.ActionDescriptor != null && filterContext.ActionDescriptor.ControllerDescriptor != null)
            {
                // look up node where key = "{controller}.{action}"
                string action = filterContext.ActionDescriptor.ActionName;
                string controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
                var node = SiteMaps.Current.FindSiteMapNodeFromKey(string.Format("{0}.{1}", controller, action));

                // if we find a node then add all paremeters into the RouteValues collection so they will match 
                if (node != null)
                {
                    foreach (var item in filterContext.RouteData.Values)
                    {
                        if (item.Key != "controller" && item.Key != "action")
                        {
                            node.RouteValues[item.Key] = item.Value;
                        }
                    }
                }
            }
            base.OnActionExecuting(filterContext);
        }
    }

@NightOwl888
Copy link
Collaborator

Ok, so you were relying on the broken "partial" matching behavior of 3.x. That has been fixed because there were several people who insisted that they needed to use the same controller and action for both the parent and child node, and the only way to get them both to work is to ensure the parent node doesn't match the request without the parameter (see this).

So you want your incoming and outgoing URLs to be different. This is exactly the problem that .NET routing was created to fix. But that doesn't mean you can't still do it using routing.

    public class IgnoreParametersRoute : Route
    {
        private readonly string[] ignoredParameters;

        public IgnoreParametersRoute(string url, object defaults, string[] ignoredParameters)
            : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
        {
            this.ignoredParameters = ignoredParameters == null ? new string[0] : ignoredParameters;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            var nonIgnoredRouteValues = new RouteValueDictionary(values.Where(param => !ignoredParameters.Contains(param.Key)).ToDictionary(t => t.Key, t => t.Value));
            return base.GetVirtualPath(requestContext, nonIgnoredRouteValues);
        }
    }

Which can be used like:

routes.Add(
    name: "Default",
    item: new IgnoreParametersRoute(
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        ignoredParameters: new string[] { "id" }
        )
);

After all, why would you want MvcSiteMapProvider's behavior to differ from how an action link would normally be created (because MVC automatically copies values over from the request just like preservedRouteParameters does).

If that doesn't suit your taste, there are several other ways to accomplish this:

  1. Create a custom URL resolver.
  2. Customize the URL matching behavior with external DI. See the demo project here.
  3. Add this feature to your own private fork of MvcSiteMapProvider.

As for a first class feature, most people do not expect their incoming and outgoing routes to be different so I doubt anyone else would find it useful. Eventually, I plan to make the matching behavior customizable, which will make things like this possible to do, but for now you can use one of these alternatives.

@fcsobel
Copy link
Author

fcsobel commented Aug 2, 2015

Allowing the matching behavior to be customized (or simplified) would be great. It's the one issue I see come up again and again. Something like ignoreRouteParameters is what came to mind but I expect it will be way more complicated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants