90

I know i can do this

var nv = HttpUtility.ParseQueryString(req.RawUrl);

But is there a way to convert this back to a url?

var newUrl = HttpUtility.Something("/page", nv);
Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174

12 Answers12

134

Simply calling ToString() on the NameValueCollection will return the name value pairs in a name1=value1&name2=value2 querystring ready format. Note that NameValueCollection types don't actually support this and it's misleading to suggest this, but the behavior works here due to the internal type that's actually returned, as explained below.

Thanks to @mjwills for pointing out that the HttpUtility.ParseQueryString method actually returns an internal HttpValueCollection object rather than a regular NameValueCollection (despite the documentation specifying NameValueCollection). The HttpValueCollection automatically encodes the querystring when using ToString(), so there's no need to write a routine that loops through the collection and uses the UrlEncode method. The desired result is already returned.

With the result in hand, you can then append it to the URL and redirect:

var nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
string url = Request.Url.AbsolutePath + "?" + nameValues.ToString();
Response.Redirect(url);

Currently the only way to use a HttpValueCollection is by using the ParseQueryString method shown above (other than reflection, of course). It looks like this won't change since the Connect issue requesting this class be made public has been closed with a status of "won't fix."

As an aside, you can call the Add, Set, and Remove methods on nameValues to modify any of the querystring items before appending it. If you're interested in that see my response to another question.

Community
  • 1
  • 1
Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
  • 4
    I'm unsure nameValues.ToString() will escape properly. –  Oct 05 '10 at 17:54
  • 10
    Actually you can just use qs.ToString(). This is because qs is not a NameValueCollection (despite the method signature of HttpUtility.ParseQueryString). It is actually a private type called System.Web.HttpValueCollection (you can check this using qs.GetType()). NameValueCollection.ToString() doesn't url encode, but HttpValueCollection.ToString() does. So your use of StringBuilder is completely unnecessary. – mjwills Nov 10 '11 at 01:23
  • @mjwills thanks for pointing that out. I wasn't aware of `HttpValueCollection` being returned and that it encodes everything. It keeps the code simple. Updated my response :) – Ahmad Mageed Nov 10 '11 at 03:15
  • 9
    My earlier comment is likely incorrect. HttpValueCollection doesn't call HttpUtility.UrlEncode - instead it seems to call HttpUtility.UrlEncodeUnicode (http://tinyurl.com/HttpValue). This is a problem since the two differ in how they handle some characters (e.g. é). The new documentation for UrlEncodeUnicode seems to suggest it shouldn't be used - http://msdn.microsoft.com/en-us/library/system.web.httputility.urlencodeunicode(v=vs.110).aspx . – mjwills Jun 28 '12 at 01:11
  • 57
    `NameValueCollection.ToString()` returns `System.Collections.Specialized.NameValueCollection` string (literally), not the "name value pairs in a querystring ready format". – Igor Brejc Dec 19 '14 at 09:12
  • 5
    @IgorBrejc thanks, I've updated my post to say it's misleading to state that `NameValueCollection` returns the expected format. The MSDN documentation is also misleading since it says `ParseQueryString` returns a `NameValueCollection` although it's actually `HttpValueCollection` and calling `ToString()` on that should return the expected format as the rest of the answer mentions. – Ahmad Mageed Dec 19 '14 at 17:22
  • 2
    I also found that NameValueCollection.ToString() does not format to a query string. – Bill Heitstuman Aug 14 '15 at 07:18
  • 2
    Following up on my earlier comment, this solution here will work reliably but **only** if you set `aspnet:DontUsePercentUUrlEncoding` to true. See http://www.andornot.com/blog/post/Beware-NameValueCollectionToString-and-DontUsePercentUUrlEncoding.aspx . – mjwills Jul 31 '17 at 00:29
  • i am late here because you alredy update the post, but i will point it out this is extremely missleading because the question is related to NameValueCollection and you answer refer to HttpValueCollection. I see you have updated the info, but the answer seems offtopic imho (if i start from a NameValueCollection, how can i get a well formatted query string? this is the question asked, as title said) – Skary Jan 03 '23 at 08:50
77
string q = String.Join("&",
             nvc.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(nvc[a])));
drzaus
  • 24,171
  • 16
  • 142
  • 201
Denis
  • 3,653
  • 4
  • 30
  • 43
  • This answer is the only one that will correctly handle encoding in the url parameters. Kudos! – ConfusedAboutCPP Jun 30 '14 at 10:20
  • 9
    also the only one that answers the literal question regarding `NameValueCollections`, rather than the provided scenario which involves `HttpValueCollection` instead – drzaus Dec 09 '14 at 18:20
  • 5
    I don't think this will work correctly if there are multiple values for the same key. – Martin Brown Mar 02 '16 at 12:57
  • 5
    @MartinBrown is right, it doesn't. Here's what works with multivalued namevaluecollections: uriBuilder.Query += String.Join("&", parameters.AllKeys.SelectMany(key => (parameters.GetValues(key) ?? Enumerable.Empty()).Select(val => String.Concat(key, "=", WebUtility.UrlEncode(val))))); – Alex Marshall Mar 30 '16 at 14:03
  • 4
    You should encode the key too: `string q = String.Join("&", nvc.AllKeys.Select(a => HttpUtility.UrlEncode(a) + "=" + HttpUtility.UrlEncode(nvc[a])));` – SoftDev Jun 05 '18 at 10:56
  • I don't think this works when the collection contains multiple values for the same name – tymtam Oct 30 '20 at 02:06
26

Make an extension method that uses a couple of loops. I prefer this solution because it's readable (no linq), doesn't require System.Web.HttpUtility, and it supports duplicate keys.

public static string ToQueryString(this NameValueCollection nvc)
{
    if (nvc == null) return string.Empty;

    StringBuilder sb = new StringBuilder();

    foreach (string key in nvc.Keys)
    {
        if (string.IsNullOrWhiteSpace(key)) continue;

        string[] values = nvc.GetValues(key);
        if (values == null) continue;

        foreach (string value in values)
        {
            sb.Append(sb.Length == 0 ? "?" : "&");
            sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
        }
    }

    return sb.ToString();
}

Example

var queryParams = new NameValueCollection()
{
    { "order_id", "0000" },
    { "item_id", "1111" },
    { "item_id", "2222" },
    { null, "skip entry with null key" },
    { "needs escaping", "special chars ? = &" },
    { "skip entry with null value", null }
};

Console.WriteLine(queryParams.ToQueryString());

Output

?order_id=0000&item_id=1111&item_id=2222&needs%20escaping=special%20chars%20%3F%20%3D%20%26
Mathew Leger
  • 1,613
  • 17
  • 22
21

This should work without too much code:

NameValueCollection nameValues = HttpUtility.ParseQueryString(String.Empty);
nameValues.Add(Request.QueryString);
// modify nameValues if desired
var newUrl = "/page?" + nameValues;

The idea is to use HttpUtility.ParseQueryString to generate an empty collection of type HttpValueCollection. This class is a subclass of NameValueCollection that is marked as internal so that your code cannot easily create an instance of it.

The nice thing about HttpValueCollection is that the ToString method takes care of the encoding for you. By leveraging the NameValueCollection.Add(NameValueCollection) method, you can add the existing query string parameters to your newly created object without having to first convert the Request.QueryString collection into a url-encoded string, then parsing it back into a collection.

This technique can be exposed as an extension method as well:

public static string ToQueryString(this NameValueCollection nameValueCollection)
{
    NameValueCollection httpValueCollection = HttpUtility.ParseQueryString(String.Empty);
    httpValueCollection.Add(nameValueCollection);
    return httpValueCollection.ToString();
}
Douglas
  • 36,802
  • 9
  • 76
  • 89
dana
  • 17,267
  • 6
  • 64
  • 88
8

Actually, you should encode the key too, not just value.

string q = String.Join("&",
nvc.AllKeys.Select(a => $"{HttpUtility.UrlEncode(a)}={HttpUtility.UrlEncode(nvc[a])}"));
Unheilig
  • 16,196
  • 193
  • 68
  • 98
user2397863
  • 350
  • 4
  • 4
5

Because a NameValueCollection can have multiple values for the same key, if you are concerned with the format of the querystring (since it will be returned as comma-separated values rather than "array notation") you may consider the following.

Example

var nvc = new NameValueCollection();
nvc.Add("key1", "val1");
nvc.Add("key2", "val2");
nvc.Add("empty", null);
nvc.Add("key2", "val2b");

Turn into: key1=val1&key2[]=val2&empty&key2[]=val2b rather than key1=val1&key2=val2,val2b&empty.

Code

string qs = string.Join("&", 
    // "loop" the keys
    nvc.AllKeys.SelectMany(k => {
        // "loop" the values
        var values = nvc.GetValues(k);
        if(values == null) return new[]{ k };
        return nvc.GetValues(k).Select( (v,i) => 
            // 'gracefully' handle formatting
            // when there's 1 or more values
            string.Format(
                values.Length > 1
                    // pick your array format: k[i]=v or k[]=v, etc
                    ? "{0}[]={1}"
                    : "{0}={1}"
                , k, HttpUtility.UrlEncode(v), i)
        );
    })
);

or if you don't like Linq so much...

string qs = nvc.ToQueryString(); // using...

public static class UrlExtensions {
    public static string ToQueryString(this NameValueCollection nvc) {
        return string.Join("&", nvc.GetUrlList());
    }

    public static IEnumerable<string> GetUrlList(this NameValueCollection nvc) {
        foreach(var k in nvc.AllKeys) {
            var values = nvc.GetValues(k);
            if(values == null)  { yield return k; continue; }
            for(int i = 0; i < values.Length; i++) {
                yield return
                // 'gracefully' handle formatting
                // when there's 1 or more values
                string.Format(
                    values.Length > 1
                        // pick your array format: k[i]=v or k[]=v, etc
                        ? "{0}[]={1}"
                        : "{0}={1}"
                    , k, HttpUtility.UrlEncode(values[i]), i);
            }
        }
    }
}

As has been pointed out in comments already, with the exception of this answer most of the other answers address the scenario (Request.QueryString is an HttpValueCollection, "not" a NameValueCollection) rather than the literal question.

Update: addressed null value issue from comment.

Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201
  • Good idea, but your linq code doesn't check for null. How can you guarantee that nvc.GetValues(k) doesn't return null? – CodingTT Oct 21 '15 at 22:48
  • @tianxu0836 good call; I assumed a key without a value would return an empty string, but guess not. – drzaus Oct 26 '15 at 10:26
3

The short answer is to use .ToString() on the NameValueCollection and combine it with the original url.

However, I'd like to point out a few things:

You cant use HttpUtility.ParseQueryString on Request.RawUrl. The ParseQueryString() method is looking for a value like this: ?var=value&var2=value2.

If you want to get a NameValueCollection of the QueryString parameters just use Request.QueryString().

var nv = Request.QueryString;

To rebuild the URL just use nv.ToString().

string url = String.Format("{0}?{1}", Request.Path, nv.ToString());

If you are trying to parse a url string instead of using the Request object use Uri and the HttpUtility.ParseQueryString method.

Uri uri = new Uri("<THE URL>");
var nv = HttpUtility.ParseQueryString(uri.Query);
string url = String.Format("{0}?{1}", uri.AbsolutePath, nv.ToString());
Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
Jeremy
  • 625
  • 8
  • 12
  • 5
    like a comment on [the accepted answer](http://stackoverflow.com/a/3866105/1037948) pointed out (and basically repeating here), `Request.QueryString` isn't actually a `NameValueCollection`, it's a special derivative `HttpValueCollection` which is why `.ToString` works; on a regular nvc you'll have to use something like [this answer](http://stackoverflow.com/a/19617268/1037948) instead – drzaus Dec 09 '14 at 18:19
2

I always use UriBuilder to convert an url with a querystring back to a valid and properly encoded url.

var url = "http://my-link.com?foo=bar";

var uriBuilder = new UriBuilder(url);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query.Add("yep", "foo&bar");

uriBuilder.Query = query.ToString();
var result = uriBuilder.ToString();

// http://my-link.com:80/?foo=bar&yep=foo%26bar
ms007
  • 4,661
  • 3
  • 28
  • 31
2

In AspNet Core 2.0 you can use QueryHelpers AddQueryString method.

Atchitutchuk
  • 174
  • 5
0

As @Atchitutchuk suggested, you can use QueryHelpers.AddQueryString in ASP.NET Core:

    public string FormatParameters(NameValueCollection parameters)
    {
        var queryString = "";
        foreach (var key in parameters.AllKeys)
        {
            foreach (var value in parameters.GetValues(key))
            {
                queryString = QueryHelpers.AddQueryString(queryString, key, value);
            }
        };

        return queryString.TrimStart('?');
    }
arni
  • 2,152
  • 19
  • 14
0

This did the trick for me:

public ActionResult SetLanguage(string language = "fr_FR")
        {            
            Request.UrlReferrer.TryReadQueryAs(out RouteValueDictionary parameters);            
            parameters["language"] = language;            
            return RedirectToAction("Index", parameters);            
        }
stan
  • 1
-2

You can use.

var ur = new Uri("/page",UriKind.Relative);

if this nv is of type string you can append to the uri first parameter. Like

var ur2 = new Uri("/page?"+nv.ToString(),UriKind.Relative);
Gabriel Guimarães
  • 2,724
  • 3
  • 27
  • 41