Consistent way to access route value parameters passed through querystring

1.5k views Asked by At

I have the following route defined in global.asax

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Portal", action = "Index", id = UrlParameter.Optional }
);

I can't control whether users access the page with "/useraccount/edit/1" or "/useraccount/edit?id=1". When using the UrlHelper Action method to generate a url, the id value is not contained in RouteData if id is passed as a querystring parameter.

new UrlHelper(helper.ViewContext.RequestContext).Action(
                            action, helper.ViewContext.RouteData.Values)

I'm looking for a consistent way to access the id value, regardless of which url was used to access the page, or a way to customize the initialization of the RouteData object so that it checks the QueryString for missing route parameters and adds them if they are found.

3

There are 3 answers

0
Failwyn On BEST ANSWER

Extending Route ended up being the easiest solution for my needs; Thank you for the suggestions! Let me know if there are any glaring issues (other than the class name) with my solution.

FrameworkRoute.cs

public class FrameworkRoute: Route
{
    public FrameworkRoute(string url, object defaults) :
        base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var routeData = base.GetRouteData(httpContext);
        if (routeData != null)
        {
            foreach (var item in routeData.Values.Where(rv => rv.Value == UrlParameter.Optional).ToList())
            {
                var val = httpContext.Request.QueryString[item.Key];
                if (!string.IsNullOrWhiteSpace(val))
                {
                    routeData.Values[item.Key] = val;
                }
            }
        }

        return routeData;
    }
}

Global.asax.cs

protected override void Application_Start()
{
       // register route
       routes.Add(new FrameworkRoute("{controller}/{action}/{id}", new { controller = "Portal", action = "Index", id = UrlParameter.Optional }));
0
BorisP On

You can use

@Url.RouteUrl("Default", new { id = ViewContext.RouteData.Values["id"] != null ? ViewContext.RouteData.Values["id"] : Request.QueryString["id"] })
0
Anton Gelenava On

try this solution

  var qs = helper.ViewContext
                .HttpContext.Request.QueryString
                .ToPairs()
                .Union(helper.ViewContext.RouteData.Values)
                .ToDictionary(x => x.Key, x => x.Value);

            var rvd = new RouteValueDictionary(qs);

            return new UrlHelper( helper.ViewContext.RequestContext).Action(action, rvd);

to convert NameValueCollection try this

public static IEnumerable<KeyValuePair<string, object>> ToPairs(this NameValueCollection collection)
        {
            if (collection == null)
            {
                throw new ArgumentNullException("collection");
            }

            return collection.Cast<string>().Select(key => new KeyValuePair<string, object>(key, collection[key]));
        }