Search code examples
c#asp.net-mvc-4urlhelper

UrlHelper.Action includes undesired additional parameters


I have a method in the controller ApplicationsController, in which I need to get the base URL for an action method:

public ActionResult MyAction(string id)
{
    var url = Url.Action("MyAction", "Applications");
    ...
}

The problem is that this includes the string id from the current route data, when I need the URL without (the URL is used to fetch content from a CMS on a URL-based lookup).

I have tried passing null and new { } as the routeValues parameter to no avail.

The matching route is as follows (above all other routes):

routes.MapLowercaseRoute(
    name: "Applications",
    url: "applications/{action}/{id}",
    defaults: new { controller = "Applications",
                    action = "Index", id = UrlParameter.Optional });

I've seen a couple of other questions touch on this but none of them seem to have a viable solution. At present, I am resorting to hardcoding the path in the controller; however, I'd like to be able to abstract this into an action filter, so I need to be able to generate the URL.

Is there a clean/conventional way to prevent this behaviour?


Solution

  • Ended up getting around this with a different approach. The only way I could come up with to prevent arbitrarily-named route values from being inserted into the generated URL was to temporarily remove them from RouteData when calling Url.Action. I've written a couple of extension methods to facilitate this:

    public static string NonContextualAction(this UrlHelper helper, string action)
    {
        return helper.NonContextualAction(action,
            helper.RequestContext.RouteData.Values["controller"].ToString());
    }
    
    public static string NonContextualAction(this UrlHelper helper, string action,
                                             string controller)
    {
        var routeValues = helper.RequestContext.RouteData.Values;
        var routeValueKeys = routeValues.Keys.Where(o => o != "controller"
                             && o != "action").ToList();
    
        // Temporarily remove routevalues
        var oldRouteValues = new Dictionary<string, object>();
        foreach (var key in routeValueKeys)
        {
            oldRouteValues[key] = routeValues[key];
            routeValues.Remove(key);
        }
    
        // Generate URL
        string url = helper.Action(routeValues["Action"].ToString(),
                                   routeValues["Controller"].ToString());
    
        // Reinsert routevalues
        foreach (var kvp in oldRouteValues)
        {
            routeValues.Add(kvp.Key, kvp.Value);
        }
    
        return url;
    }
    

    This allows me to do this in an action filter where I won't necessarily know what the parameter names for the action are (and therefore can't just pass an anonymous object as in the other answers).

    Still very much interested to know if someone has a more elegant solution, however.