UrlHelper Extension to Modify Query String

The following extension method allows you to add, update, or remove parameters from a URL's query string.

public static class UrlHelperExtensions
{
    /// <summary>
    /// Add, update, or remove parameters from a URL's query string.
    /// </summary>
    /// <param name="helper">UrlHelper instance</param>
    /// <param name="url">The URL to modify. If null, the current URL from the Request object is used.</param>
    /// <param name="updates">Query string parameters to add/overwrite.</param>
    /// <param name="removes">Query string parameters to remove entirely.</param>
    /// <param name="appends">Query string parameters to append additional values to (using delimiter)</param>
    /// <param name="subtracts">Query string parameters to subtract values from (using delimiter)</param>
    /// <param name="delimiter">Character to use to delimit multiple values for a query string parameter (defaults to `|`)</param>
    /// <returns>URL with modified query string</returns>
    public static string ModifyQueryString(this UrlHelper helper,
        string url,
        IDictionary<string, object> updates = null,
        IEnumerable<string> removes = null,
        IDictionary<string, object> appends = null,
        IDictionary<string, object> subtracts = null,
        char delimiter = '|')
    {
        var request = helper.RequestContext.HttpContext.Request;

        if (string.IsNullOrWhiteSpace(url))
        {
            url = request.RawUrl;
        }

        var urlParts = url.Split('?');
        url = urlParts[0];
        var query = urlParts.Length > 1
            ? HttpUtility.ParseQueryString(urlParts[1])
            : new NameValueCollection();

        if (updates != null)
        {
            updates.Keys.ToList().ForEach(key => query[key] = updates[key].ToString());
        }

        if (removes != null)
        {
            removes.ToList().ForEach(key => query.Remove(key));
        }

        if (appends != null)
        {
            foreach (var key in appends.Keys)
            {
                var values = new List<string>();
                if (query.AllKeys.Contains(key))
                {
                    values.Add(query[key]);
                }
                if (typeof(IList).IsAssignableFrom(appends[key].GetType()))
                {
                    foreach (var item in (appends[key] as IList))
                    {
                        values.Add(item.ToString());
                    }
                }
                else
                {
                    values.Add(appends[key].ToString());
                }
                query[key] = string.Join(delimiter.ToString(), values);
            }
        }

        if (subtracts != null)
        {
            foreach (var key in subtracts.Keys)
            {
                if (query.AllKeys.Contains(key))
                {
                    var queryParts = query[key].Split(new char[] { delimiter }, StringSplitOptions.RemoveEmptyEntries).ToList();
                    if (typeof(IList).IsAssignableFrom(subtracts[key].GetType()))
                    {
                        foreach (var item in (subtracts[key] as IList))
                        {
                            queryParts.Remove(item.ToString());
                        }
                    }
                    else
                    {
                        queryParts.Remove(subtracts[key].ToString());
                    }
                    query[key] = string.Join(delimiter.ToString(), queryParts);
                }
            }
        }

        var queryString = string.Join("&",
            query.AllKeys.Where(key =>
                !string.IsNullOrWhiteSpace(query[key])).Select(key =>
                    string.Join("&", query.GetValues(key).Select(val =>
                        string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(val))))));

        return query.HasKeys() ? url + "?" + queryString : url;
    }
comments powered by Disqus