3

I use this extension to create an RouteUrl with the current query-string appended to it. This works fine for query-strings which does not have the same key twice or more.

public static string CurrentQueryStringRouteUrl(this UrlHelper url, string routeName, RouteValueDictionary routeValues)
{
    var context = url.RequestContext;
    var combinedRouteValues = new RouteValueDictionary();
    var queryString = context.HttpContext.Request.QueryString;

    foreach (var key in queryString.AllKeys.Where(key => key != null))
    {
        combinedRouteValues[key] = queryString[key];
    }

    if (routeValues != null)
    {
        foreach (var routeValue in routeValues)
        {
            combinedRouteValues[routeValue.Key] = routeValue.Value;
        }
    }

    return url.RouteUrl(routeName, combinedRouteValues);
}

When there is query-string keys of same name, e.g. ?id=1&id=2&id=3, this is converted into ?id=1,2,3 using the method above. Is there any way to avoid that? I wish to keep the original query-string as I am binding these values on a model list.

I am aware I can create a custom model-binder to bind the comma separated string to string[] (or int[] in this example) but I wish to avoid this as far of consistence goes.

janhartmann
  • 14,713
  • 15
  • 82
  • 138

2 Answers2

0

You can to do this, but it is dirty hack:

string _rawstring = context.HttpContext.Request.RawUrl;
int _f;
_f = _rawstring.IndexOf('?');
string _resultString = _rawstring.SubString(_f, _rawstring.Length);

Here you can find helpful info about that problem: How to deal with more than one value per key in ASP.NET MVC 3?

Community
  • 1
  • 1
0

I took a slightly different approach, because I really needed separate duplicated keys in my query string. I alter the key using a counter, and then after rendering the url string, I restore the original parameter names. I needed this for GridMvc's grid_filter queries, but you can adapt it for your purpose.

/// <summary>
/// Allows you to create or extend a collection of route values to use in a url action
/// </summary>
public class RouteValueBuilder
{
    readonly RouteValueDictionary routeValues;
    private int gridFilterCounter = 0;

    public RouteValueBuilder(object existingRouteValues = null)
    {
        routeValues = existingRouteValues as RouteValueDictionary ?? new RouteValueDictionary(existingRouteValues);
    }

    public void Add(string field, object value)
    {
        if (field == "grid_filter" && routeValues.ContainsKey(field))
        {
            // Because we can't add duplicate keys and GridMvc doesn't support joined comma format for query strings,
            // we briefly rename each new filter, and then the Finalise method must be called after the url
            // string is rendered to restore the grid_filter names back to normal.
            gridFilterCounter++;
            routeValues.Add(field + gridFilterCounter, value);
        }
        else if (routeValues.ContainsKey(field))
        {
            // Since duplicate key names are not supported, the concatenated comma approach can be used
            routeValues[field] += "," + value;
        }
        else
        {
            routeValues.Add(field, value);
        }
    }

    public RouteValueDictionary Get()
    {
        return routeValues;
    }

    /// <summary>
    /// Cleans up the final string url, fixing workarounds done during the building process.
    /// This must be called after the final url string is rendered.
    /// </summary>
    public static string Finalise(string url)
    {
        // Restores grid_filter parameters to their correct naming.  See comments on Add method.
        for (var i = 0; i < 100; i++)
        {
            url = url.Replace("grid_filter" + i, "grid_filter");
        }

        return url;
    }
}

Usage:

var builder = new RouteValueBuilder();
builder.Add("grid_filter", "value1");
builder.Add("grid_filter", "value2");
string url = Html.Action("Index", "Home", builder.Get());
url = RouteValueBuilder.Finalise(url);

Edit: Note that the comma concatenation approach actually doesn't work in the class because it gets encoded, but the support for duplication is the main taker from this example.

Savage
  • 2,296
  • 2
  • 30
  • 40