How to build a query string for a URL in C#?
C#.NetUrlQuery StringC# Problem Overview
A common task when calling web resources from a code is building a query string to including all the necessary parameters. While by all means no rocket science, there are some nifty details you need to take care of like, appending an &
if not the first parameter, encoding the parameters etc.
The code to do it is very simple, but a bit tedious:
StringBuilder SB = new StringBuilder();
if (NeedsToAddParameter A)
{
SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA"));
}
if (NeedsToAddParameter B)
{
if (SB.Length>0) SB.Append("&");
SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); }
}
This is such a common task one would expect a utility class to exist that makes it more elegant and readable. Scanning MSDN, I failed to find one—which brings me to the following question:
What is the most elegant clean way you know of doing the above?
C# Solutions
Solution 1 - C#
You can create a new writeable instance of HttpValueCollection
by calling System.Web.HttpUtility.ParseQueryString(string.Empty)
, and then use it as any NameValueCollection
. Once you have added the values you want, you can call ToString
on the collection to get a query string, as follows:
NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);
queryString.Add("key1", "value1");
queryString.Add("key2", "value2");
return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded
The HttpValueCollection
is internal and so you cannot directly construct an instance. However, once you obtain an instance you can use it like any other NameValueCollection
. Since the actual object you are working with is an HttpValueCollection
, calling ToString method will call the overridden method on HttpValueCollection
, which formats the collection as a URL-encoded query string.
After searching SO and the web for an answer to a similar issue, this is the most simple solution I could find.
.NET Core
If you're working in .NET Core, you can use the Microsoft.AspNetCore.WebUtilities.QueryHelpers
class, which simplifies this greatly.
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers
Sample Code:
const string url = "https://customer-information.azure-api.net/customers/search/taxnbr";
var param = new Dictionary<string, string>() { { "CIKey", "123456789" } };
var newUrl = new Uri(QueryHelpers.AddQueryString(url, param));
Solution 2 - C#
If you look under the hood the QueryString property is a NameValueCollection. When I've done similar things I've usually been interested in serialising AND deserialising so my suggestion is to build a NameValueCollection up and then pass to:
using System.Linq;
using System.Web;
using System.Collections.Specialized;
private string ToQueryString(NameValueCollection nvc)
{
var array = (
from key in nvc.AllKeys
from value in nvc.GetValues(key)
select string.Format(
"{0}={1}",
HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(value))
).ToArray();
return "?" + string.Join("&", array);
}
I imagine there's a super elegant way to do this in LINQ too...
Solution 3 - C#
With the inspiration from Roy Tinker's comment, I ended up using a simple extension method on the Uri class that keeps my code concise and clean:
using System.Web;
public static class HttpExtensions
{
public static Uri AddQuery(this Uri uri, string name, string value)
{
var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);
httpValueCollection.Remove(name);
httpValueCollection.Add(name, value);
var ub = new UriBuilder(uri);
ub.Query = httpValueCollection.ToString();
return ub.Uri;
}
}
Usage:
Uri url = new Uri("http://localhost/rest/something/browse").
AddQuery("page", "0").
AddQuery("pageSize", "200");
Edit - Standards compliant variant
As several people pointed out, httpValueCollection.ToString()
encodes Unicode characters in a non-standards-compliant way. This is a variant of the same extension method that handles such characters by invoking HttpUtility.UrlEncode
method instead of the deprecated HttpUtility.UrlEncodeUnicode
method.
using System.Web;
public static Uri AddQuery(this Uri uri, string name, string value)
{
var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);
httpValueCollection.Remove(name);
httpValueCollection.Add(name, value);
var ub = new UriBuilder(uri);
// this code block is taken from httpValueCollection.ToString() method
// and modified so it encodes strings with HttpUtility.UrlEncode
if (httpValueCollection.Count == 0)
ub.Query = String.Empty;
else
{
var sb = new StringBuilder();
for (int i = 0; i < httpValueCollection.Count; i++)
{
string text = httpValueCollection.GetKey(i);
{
text = HttpUtility.UrlEncode(text);
string val = (text != null) ? (text + "=") : string.Empty;
string[] vals = httpValueCollection.GetValues(i);
if (sb.Length > 0)
sb.Append('&');
if (vals == null || vals.Length == 0)
sb.Append(val);
else
{
if (vals.Length == 1)
{
sb.Append(val);
sb.Append(HttpUtility.UrlEncode(vals[0]));
}
else
{
for (int j = 0; j < vals.Length; j++)
{
if (j > 0)
sb.Append('&');
sb.Append(val);
sb.Append(HttpUtility.UrlEncode(vals[j]));
}
}
}
}
}
ub.Query = sb.ToString();
}
return ub.Uri;
}
Solution 4 - C#
Flurl [disclosure: I'm the author] supports building query strings via anonymous objects (among other ways):
var url = "http://www.some-api.com".SetQueryParams(new
{
api_key = ConfigurationManager.AppSettings["SomeApiKey"],
max_results = 20,
q = "Don't worry, I'll get encoded!"
});
The optional Flurl.Http companion lib allows you to do HTTP calls right off the same fluent call chain, extending it into a full-blown REST client:
T result = await "https://api.mysite.com"
.AppendPathSegment("person")
.SetQueryParams(new { ap_key = "my-key" })
.WithOAuthBearerToken("MyToken")
.PostJsonAsync(new { first_name = firstName, last_name = lastName })
.ReceiveJson<T>();
The full package is available on NuGet:
PM> Install-Package Flurl.Http
or just the stand-alone URL builder:
PM> Install-Package Flurl
Solution 5 - C#
I answered a similar question a while ago. Basically, the best way would be to use the class HttpValueCollection
, which ASP.NET's Request.QueryString
property actually is, unfortunately it is internal in the .NET framework.
You could use Reflector to grab it (and place it into your Utils class). This way you could manipulate the query string like a NameValueCollection, but with all the url encoding/decoding issues taken care for you.
HttpValueCollection
extends NameValueCollection
, and has a constructor that takes an encoded query string (ampersands and question marks included), and it overrides a ToString()
method to later rebuild the query string from the underlying collection.
Example:
var coll = new HttpValueCollection();
coll["userId"] = "50";
coll["paramA"] = "A";
coll["paramB"] = "B";
string query = coll.ToString(true); // true means use urlencode
Console.WriteLine(query); // prints: userId=50¶mA=A¶mB=B
Solution 6 - C#
Here's a fluent/lambda-ish way as an extension method (combining concepts in previous posts) that supports multiple values for the same key. My personal preference is extensions over wrappers for discover-ability by other team members for stuff like this. Note that there's controversy around encoding methods, plenty of posts about it on Stack Overflow (one such post) and MSDN bloggers (like this one).
public static string ToQueryString(this NameValueCollection source)
{
return String.Join("&", source.AllKeys
.SelectMany(key => source.GetValues(key)
.Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))))
.ToArray());
}
edit: with null support, though you'll probably need to adapt it for your particular situation
public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries)
{
return source != null ? String.Join("&", source.AllKeys
.Where(key => !removeEmptyEntries || source.GetValues(key)
.Where(value => !String.IsNullOrEmpty(value))
.Any())
.SelectMany(key => source.GetValues(key)
.Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value))
.Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty)))
.ToArray())
: string.Empty;
}
Solution 7 - C#
Here's my late entry. I didn't like any of the others for various reasons, so I wrote my own.
This version features:
-
Use of StringBuilder only. No ToArray() calls or other extension methods. It doesn't look as pretty as some of the other responses, but I consider this a core function so efficiency is more important than having "fluent", "one-liner" code which hide inefficiencies.
-
Handles multiple values per key. (Didn't need it myself but just to silence Mauricio ;)
public string ToQueryString(NameValueCollection nvc) { StringBuilder sb = new StringBuilder("?"); bool first = true; foreach (string key in nvc.AllKeys) { foreach (string value in nvc.GetValues(key)) { if (!first) { sb.Append("&"); } sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value)); first = false; } } return sb.ToString(); }
Example Usage
var queryParams = new NameValueCollection()
{
{ "x", "1" },
{ "y", "2" },
{ "foo", "bar" },
{ "foo", "baz" },
{ "special chars", "? = &" },
};
string url = "http://example.com/stuff" + ToQueryString(queryParams);
Console.WriteLine(url);
Output
http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26
Solution 8 - C#
Curious that no one has mentioned QueryBuilder from AspNet.Core.
It's helpful when you have a query with duplicate key like ?filter=a&filter=b
var qb = new QueryBuilder();
qb.Add("filter", new string[] {"A", "B"};
Then you'll just add qb to the URI, it is converted automatically to string.
Solution 9 - C#
I needed to solve the same problem for a portable class library (PCL) that I'm working on. In this case, I don't have access to System.Web so I can't use ParseQueryString.
Instead I used System.Net.Http.FormUrlEncodedContent
like so:
var url = new UriBuilder("http://example.com");
url.Query = new FormUrlEncodedContent(new Dictionary<string,string>()
{
{"param1", "val1"},
{"param2", "val2"},
{"param3", "val3"},
}).ReadAsStringAsync().Result;
Solution 10 - C#
How about creating extension methods that allow you to add the parameters in a fluent style like this?
string a = "http://www.somedomain.com/somepage.html"
.AddQueryParam("A", "TheValueOfA")
.AddQueryParam("B", "TheValueOfB")
.AddQueryParam("Z", "TheValueOfZ");
string b = new StringBuilder("http://www.somedomain.com/anotherpage.html")
.AddQueryParam("A", "TheValueOfA")
.AddQueryParam("B", "TheValueOfB")
.AddQueryParam("Z", "TheValueOfZ")
.ToString();
Here's the overload that uses a string
:
public static string AddQueryParam(
this string source, string key, string value)
{
string delim;
if ((source == null) || !source.Contains("?"))
{
delim = "?";
}
else if (source.EndsWith("?") || source.EndsWith("&"))
{
delim = string.Empty;
}
else
{
delim = "&";
}
return source + delim + HttpUtility.UrlEncode(key)
+ "=" + HttpUtility.UrlEncode(value);
}
And here's the overload that uses a StringBuilder
:
public static StringBuilder AddQueryParam(
this StringBuilder source, string key, string value)
{
bool hasQuery = false;
for (int i = 0; i < source.Length; i++)
{
if (source[i] == '?')
{
hasQuery = true;
break;
}
}
string delim;
if (!hasQuery)
{
delim = "?";
}
else if ((source[source.Length - 1] == '?')
|| (source[source.Length - 1] == '&'))
{
delim = string.Empty;
}
else
{
delim = "&";
}
return source.Append(delim).Append(HttpUtility.UrlEncode(key))
.Append("=").Append(HttpUtility.UrlEncode(value));
}
Solution 11 - C#
public static string ToQueryString(this Dictionary<string, string> source)
{
return String.Join("&", source.Select(kvp => String.Format("{0}={1}", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))).ToArray());
}
public static string ToQueryString(this NameValueCollection source)
{
return String.Join("&", source.Cast<string>().Select(key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(source[key]))).ToArray());
}
Solution 12 - C#
Add this class to your project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
public class QueryStringBuilder
{
private readonly List<KeyValuePair<string, object>> _list;
public QueryStringBuilder()
{
_list = new List<KeyValuePair<string, object>>();
}
public void Add(string name, object value)
{
_list.Add(new KeyValuePair<string, object>(name, value));
}
public override string ToString()
{
return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key), "=", Uri.EscapeDataString(kvp.Value.ToString()))));
}
}
And use it like this:
var actual = new QueryStringBuilder {
{"foo", 123},
{"bar", "val31"},
{"bar", "val32"}
};
actual.Add("a+b", "c+d");
actual.ToString(); // "foo=123&bar=val31&bar=val32&a%2bb=c%2bd"
Solution 13 - C#
Untested, but I think something along these lines would work quite nicely
public class QueryString
{
private Dictionary<string,string> _Params = new Dictionary<string,string>();
public overide ToString()
{
List<string> returnParams = new List<string>();
foreach (KeyValuePair param in _Params)
{
returnParams.Add(String.Format("{0}={1}", param.Key, param.Value));
}
// return String.Format("?{0}", String.Join("&", returnParams.ToArray()));
// credit annakata
return "?" + String.Join("&", returnParams.ToArray());
}
public void Add(string key, string value)
{
_Params.Add(key, HttpUtility.UrlEncode(value));
}
}
QueryString query = new QueryString();
query.Add("param1", "value1");
query.Add("param2", "value2");
return query.ToString();
Solution 14 - C#
My offering:
public static Uri AddQuery(this Uri uri, string name, string value)
{
// this actually returns HttpValueCollection : NameValueCollection
// which uses unicode compliant encoding on ToString()
var query = HttpUtility.ParseQueryString(uri.Query);
query.Add(name, value);
var uriBuilder = new UriBuilder(uri)
{
Query = query.ToString()
};
return uriBuilder.Uri;
}
Usage:
var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method")
.AddQuery("wow", "soFluent");
// http://stackoverflow.com?such=method&wow=soFluent
Solution 15 - C#
In dotnet core QueryHelpers.AddQueryString() will accept an IDictionary
var queryParams = new SortedList<string,string>(2);
queryParams.Add("abc", "val1");
queryParams.Add("def", "val2");
string requestUri = QueryHelpers.AddQueryString("https://localhost/api", queryParams);
Solution 16 - C#
Combined the top answers to create an anonymous object version:
var queryString = HttpUtility2.BuildQueryString(new
{
key2 = "value2",
key1 = "value1",
});
That generates this:
>> key2=value2&key1=value1
Here's the code:
public static class HttpUtility2
{
public static string BuildQueryString<T>(T obj)
{
var queryString = HttpUtility.ParseQueryString(string.Empty);
foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>())
{
var value = (property.GetValue(obj) ?? "").ToString();
queryString.Add(property.Name, value);
}
return queryString.ToString();
}
}
Solution 17 - C#
A quick extension method based version:
class Program
{
static void Main(string[] args)
{
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("A", "AValue"),
new KeyValuePair<string, string>("B", "BValue")
};
string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray());
}
}
public static class KeyValueExtensions
{
public static string ToQueryString(this KeyValuePair<string, string> obj)
{
return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value);
}
}
You could use a where clause to select which parameters get added to the string.
Solution 18 - C#
There's lots of good answers here but for those using modern C# this may be a nice utility class to keep around.
public class QueryParamBuilder
{
private readonly Dictionary<string, string> _fields = new();
public QueryParamBuilder Add(string key, string value)
{
_fields.Add(key, value);
return this;
}
public string Build()
{
return $"?{String.Join("&", _fields.Select(pair => $"{HttpUtility.UrlEncode(pair.Key)}={HttpUtility.UrlEncode(pair.Value)}"))}";
}
public static QueryParamBuilder New => new();
}
I use an internal Dictionary
here because dictionaries are enumerable key value pairs internally which makes iterating over them much easier than a NameValueCollection
.
Then the query string itself is a simple interpolated string with a join.
Additionally I provide a static interface into the constructor to make the construction of a new builder very easy and only allow one exposed method Add
to add new query parameter values. Finally you terminate the chain with Build()
to actually get the final string.
Here's an example of its usage
var queryString = QueryParamBuilder.New
.Add("id", "0123")
.Add("value2", 1234.ToString())
.Add("valueWithSpace","value with spa12!@#@!ce")
.Build();
The result is as expected
?id=0123&value2=1234&valueWithSpace=value+with+spa12!%40%23%40!ce
Hopefully some of you will find this nice and elegant.
Solution 19 - C#
Assuming that you want to reduce dependencies to other assemblies and to keep things simple, you can do:
var sb = new System.Text.StringBuilder();
sb.Append("a=" + HttpUtility.UrlEncode("TheValueOfA") + "&");
sb.Append("b=" + HttpUtility.UrlEncode("TheValueOfB") + "&");
sb.Append("c=" + HttpUtility.UrlEncode("TheValueOfC") + "&");
sb.Append("d=" + HttpUtility.UrlEncode("TheValueOfD") + "&");
sb.Remove(sb.Length-1, 1); // Remove the final '&'
string result = sb.ToString();
This works well with loops too. The final ampersand removal needs to go outside of the loop.
Note that the concatenation operator is used to improve readability. The cost of using it compared to the cost of using a StringBuilder is minimal (I think Jeff Atwood posted something on this topic).
Solution 20 - C#
Chain-able wrapper class for HttpValueCollection:
namespace System.Web.Mvc {
public class QueryStringBuilder {
private NameValueCollection collection;
public QueryStringBuilder() {
collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
}
public QueryStringBuilder Add(string key, string value) {
collection.Add(key, value);
return this;
}
public QueryStringBuilder Remove(string key) {
collection.Remove(key);
return this;
}
public string this[string key] {
get { return collection[key]; }
set { collection[key] = value; }
}
public string ToString() {
return collection.ToString();
}
}
}
Example usage:
QueryStringBuilder parameters = new QueryStringBuilder()
.Add("view", ViewBag.PageView)
.Add("page", ViewBag.PageNumber)
.Add("size", ViewBag.PageSize);
string queryString = parameters.ToString();
Solution 21 - C#
I have an extension method for Uri that:
- Accepts anonymous objects:
uri.WithQuery(new { name = "value" })
- Accepts collections of
string/string
pairs (e.g. Dictionary`2). - Accepts collections of
string/object
pairs (e.g. RouteValueDictionary). - Accepts NameValueCollections.
- Sorts the query values by key so the same values produce equal URIs.
- Supports multiple values per key, preserving their original order.
The documented version can be found here.
The extension:
public static Uri WithQuery(this Uri uri, object values)
{
if (uri == null)
throw new ArgumentNullException(nameof(uri));
if (values != null)
{
var query = string.Join(
"&", from p in ParseQueryValues(values)
where !string.IsNullOrWhiteSpace(p.Key)
let k = HttpUtility.UrlEncode(p.Key.Trim())
let v = HttpUtility.UrlEncode(p.Value)
orderby k
select string.IsNullOrEmpty(v) ? k : $"{k}={v}");
if (query.Length != 0 || uri.Query.Length != 0)
uri = new UriBuilder(uri) { Query = query }.Uri;
}
return uri;
}
The query parser:
private static IEnumerable<KeyValuePair<string, string>> ParseQueryValues(object values)
{
// Check if a name/value collection.
var nvc = values as NameValueCollection;
if (nvc != null)
return from key in nvc.AllKeys
from val in nvc.GetValues(key)
select new KeyValuePair<string, string>(key, val);
// Check if a string/string dictionary.
var ssd = values as IEnumerable<KeyValuePair<string, string>>;
if (ssd != null)
return ssd;
// Check if a string/object dictionary.
var sod = values as IEnumerable<KeyValuePair<string, object>>;
if (sod == null)
{
// Check if a non-generic dictionary.
var ngd = values as IDictionary;
if (ngd != null)
sod = ngd.Cast<dynamic>().ToDictionary<dynamic, string, object>(
p => p.Key.ToString(), p => p.Value as object);
// Convert object properties to dictionary.
if (sod == null)
sod = new RouteValueDictionary(values);
}
// Normalize and return the values.
return from pair in sod
from val in pair.Value as IEnumerable<string>
?? new[] { pair.Value?.ToString() }
select new KeyValuePair<string, string>(pair.Key, val);
}
Here are the tests:
var uri = new Uri("https://stackoverflow.com/yo?oldKey=oldValue");
// Test with a string/string dictionary.
var q = uri.WithQuery(new Dictionary<string, string>
{
["k1"] = string.Empty,
["k2"] = null,
["k3"] = "v3"
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1&k2&k3=v3"));
// Test with a string/object dictionary.
q = uri.WithQuery(new Dictionary<string, object>
{
["k1"] = "v1",
["k2"] = new[] { "v2a", "v2b" },
["k3"] = null
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3"));
// Test with a name/value collection.
var nvc = new NameValueCollection()
{
["k1"] = string.Empty,
["k2"] = "v2a"
};
nvc.Add("k2", "v2b");
q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1&k2=v2a&k2=v2b"));
// Test with any dictionary.
q = uri.WithQuery(new Dictionary<int, HashSet<string>>
{
[1] = new HashSet<string> { "v1" },
[2] = new HashSet<string> { "v2a", "v2b" },
[3] = null
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?1=v1&2=v2a&2=v2b&3"));
// Test with an anonymous object.
q = uri.WithQuery(new
{
k1 = "v1",
k2 = new[] { "v2a", "v2b" },
k3 = new List<string> { "v3" },
k4 = true,
k5 = null as Queue<string>
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3=v3&k4=True&k5"));
// Keep existing query using a name/value collection.
nvc = HttpUtility.ParseQueryString(uri.Query);
nvc.Add("newKey", "newValue");
q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?newKey=newValue&oldKey=oldValue"));
// Merge two query objects using the RouteValueDictionary.
var an1 = new { k1 = "v1" };
var an2 = new { k2 = "v2" };
q = uri.WithQuery(
new RouteValueDictionary(an1).Concat(
new RouteValueDictionary(an2)));
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2"));
Solution 22 - C#
Same as accepted solution, but transfred to "dot" LINQ syntax...
private string ToQueryString(NameValueCollection nvc)
{
if (nvc == null) return String.Empty;
var queryParams =
string.Join("&", nvc.AllKeys.Select(key =>
string.Join("&", nvc.GetValues(key).Select(v => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(v))))));
return "?" + queryParams;
}
Solution 23 - C#
While not elegant, I opted for a simpler version that doesn't use NameValueCollecitons
- just a builder pattern wrapped around StringBuilder
.
public class UrlBuilder
{
#region Variables / Properties
private readonly StringBuilder _builder;
#endregion Variables / Properties
#region Constructor
public UrlBuilder(string urlBase)
{
_builder = new StringBuilder(urlBase);
}
#endregion Constructor
#region Methods
public UrlBuilder AppendParameter(string paramName, string value)
{
if (_builder.ToString().Contains("?"))
_builder.Append("&");
else
_builder.Append("?");
_builder.Append(HttpUtility.UrlEncode(paramName));
_builder.Append("=");
_builder.Append(HttpUtility.UrlEncode(value));
return this;
}
public override string ToString()
{
return _builder.ToString();
}
#endregion Methods
}
Per existing answers, I made sure to use HttpUtility.UrlEncode
calls. It's used like so:
string url = new UrlBuilder("http://www.somedomain.com/")
.AppendParameter("a", "true")
.AppendParameter("b", "muffin")
.AppendParameter("c", "muffin button")
.ToString();
// Result: http://www.somedomain.com?a=true&b=muffin&c=muffin%20button
Solution 24 - C#
The query string can be added to a URL by:
- create a name value collection object
- add the query string items and their values to this object
- encode this name value collection object to the url the code is provided in the below link
https://blog.codingnovice.com/blog
public ActionResult Create()
{
//declaring name value collection object
NameValueCollection collection = new NameValueCollection();
//adding new value to the name value collection object
collection.Add("Id1", "wwe323");
collection.Add("Id2", "454w");
collection.Add("Id3", "tyt5656");
collection.Add("Id4", "343wdsd");
//generating query string
string url = GenerateQueryString(collection);
return View();
}
private string GenerateQueryString(NameValueCollection collection)
{
var querystring = (
from key in collection.AllKeys
from value in collection.GetValues(key)
select string.Format("{0}={1}",
HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(value))
).ToArray();
return "?" + string.Join("&", querystring);
}
Solution 25 - C#
I added the following method to my PageBase class.
protected void Redirect(string url)
{
Response.Redirect(url);
}
protected void Redirect(string url, NameValueCollection querystrings)
{
StringBuilder redirectUrl = new StringBuilder(url);
if (querystrings != null)
{
for (int index = 0; index < querystrings.Count; index++)
{
if (index == 0)
{
redirectUrl.Append("?");
}
redirectUrl.Append(querystrings.Keys[index]);
redirectUrl.Append("=");
redirectUrl.Append(HttpUtility.UrlEncode(querystrings[index]));
if (index < querystrings.Count - 1)
{
redirectUrl.Append("&");
}
}
}
this.Redirect(redirectUrl.ToString());
}
To call:
NameValueCollection querystrings = new NameValueCollection();
querystrings.Add("language", "en");
querystrings.Add("id", "134");
this.Redirect("http://www.mypage.com", querystrings);
Solution 26 - C#
I wrote some extension methods that I have found very useful when working with QueryStrings. Often I want to start with the current QueryString and modify before using it. Something like,
var res = Request.QueryString.Duplicate()
.ChangeField("field1", "somevalue")
.ChangeField("field2", "only if following is true", true)
.ChangeField("id", id, id>0)
.WriteLocalPathWithQuery(Request.Url)); //Uses context to write the path
For more and the source: http://www.charlesrcook.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx
It's basic, but I like the style.
Solution 27 - C#
Just wanted to throw in my 2 cents:
public static class HttpClientExt
{
public static Uri AddQueryParams(this Uri uri, string query)
{
var ub = new UriBuilder(uri);
ub.Query = string.IsNullOrEmpty(uri.Query) ? query : string.Join("&", uri.Query.Substring(1), query);
return ub.Uri;
}
public static Uri AddQueryParams(this Uri uri, IEnumerable<string> query)
{
return uri.AddQueryParams(string.Join("&", query));
}
public static Uri AddQueryParams(this Uri uri, string key, string value)
{
return uri.AddQueryParams(string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)));
}
public static Uri AddQueryParams(this Uri uri, params KeyValuePair<string,string>[] kvps)
{
return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
}
public static Uri AddQueryParams(this Uri uri, IDictionary<string, string> kvps)
{
return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
}
public static Uri AddQueryParams(this Uri uri, NameValueCollection nvc)
{
return uri.AddQueryParams(nvc.AllKeys.SelectMany(nvc.GetValues, (key, value) => string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))));
}
}
The docs say that uri.Query
will start with a ?
if it's non-empty and you should trim it off if you're going to modify it.
Note that HttpUtility.UrlEncode
is found in System.Web
.
Usage:
var uri = new Uri("https://api.del.icio.us/v1/posts/suggest").AddQueryParam("url","http://stackoverflow.com")
Solution 28 - C#
// USAGE
[TestMethod]
public void TestUrlBuilder()
{
Console.WriteLine(
new UrlBuilder("http://www.google.com?A=B")
.AddPath("SomePathName")
.AddPath("AnotherPathName")
.SetQuery("SomeQueryKey", "SomeQueryValue")
.AlterQuery("A", x => x + "C"));
}
Output:
> http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue
The code; you can all thank me somewhere, somehow :D
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
// By Demetris Leptos
namespace TheOperator.Foundation.Web
{
public class UrlBuilder
{
public string Scheme { get; set; }
public string Host { get; set; }
public int? Port { get; set; }
public List<string> Paths { get; set; }
public SortedDictionary<string, string> QueryPairs { get; set; }
public UrlBuilder(string url)
{
this.Paths = new List<string>();
this.QueryPairs = new SortedDictionary<string, string>();
string path = null;
string query = null;
Uri relativeUri = null;
if (!Uri.TryCreate(url, UriKind.Relative, out relativeUri))
{
var uriBuilder = new UriBuilder(url);
this.Scheme = uriBuilder.Scheme;
this.Host = uriBuilder.Host;
this.Port = uriBuilder.Port;
path = uriBuilder.Path;
query = uriBuilder.Query;
}
else
{
var queryIndex = url.IndexOf('?');
if (queryIndex >= 0)
{
path = url.Substring(0, queryIndex);
query = url.Substring(queryIndex + 1);
}
else
{
path = url;
}
}
this.Paths.AddRange(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));
if (query != null)
{
var queryKeyValuePairs = HttpUtility.ParseQueryString(query);
foreach (var queryKey in queryKeyValuePairs.AllKeys)
{
this.QueryPairs[queryKey] = queryKeyValuePairs[queryKey];
}
}
}
public UrlBuilder AddPath(string value)
{
this.Paths.Add(value);
return this;
}
public UrlBuilder SetQuery(string key, string value)
{
this.QueryPairs[key] = value;
return this;
}
public UrlBuilder RemoveQuery(string key)
{
this.QueryPairs.Remove(key);
return this;
}
public UrlBuilder AlterQuery(string key, Func<string, string> alterMethod, bool removeOnNull = false)
{
string value;
this.QueryPairs.TryGetValue(key, out value);
value = alterMethod(value);
if (removeOnNull && value == null)
{
return this.RemoveQuery(key);
}
else
{
return this.SetQuery(key, value);
}
}
public override string ToString()
{
var path = !string.IsNullOrWhiteSpace(this.Host)
? string.Join("/", this.Host, string.Join("/", this.Paths))
: string.Join("/", this.Paths);
var query = string.Join("&", this.QueryPairs.Select(x => string.Concat(x.Key, "=", HttpUtility.UrlEncode(x.Value))));
return string.Concat(
!string.IsNullOrWhiteSpace(this.Scheme) ? string.Concat(this.Scheme, "://") : null,
path,
!string.IsNullOrWhiteSpace(query) ? string.Concat("?", query) : null);
}
}
}
Solution 29 - C#
I went with the solution proposed by DSO (answered on Aug 2 '11 at 7:29), his solution does not require using HttpUtility. However, as per an article posted in Dotnetpearls, using a Dictionary is faster (in performance) than using NameValueCollection. Here is DSO's solution modified to use Dictionary in place of NameValueCollection.
public static Dictionary<string, string> QueryParametersDictionary()
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("name", "John Doe");
dictionary.Add("address.city", "Seattle");
dictionary.Add("address.state_code", "WA");
dictionary.Add("api_key", "5352345263456345635");
return dictionary;
}
public static string ToQueryString(Dictionary<string, string> nvc)
{
StringBuilder sb = new StringBuilder();
bool first = true;
foreach (KeyValuePair<string, string> pair in nvc)
{
if (!first)
{
sb.Append("&");
}
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value));
first = false;
}
return sb.ToString();
}
Solution 30 - C#
I wrote a helper for my razor project using some of the hints from other answers.
The ParseQueryString business is necessary because we are not allowed to tamper with the QueryString object of the current request.
@helper GetQueryStringWithValue(string key, string value) {
var queryString = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString());
queryString[key] = value;
@Html.Raw(queryString.ToString())
}
I use it like this:
location.search = '[email protected]("var-name", "var-value")';
If you want it to take more than one value, just change the parameters to a Dictionary and add the pairs to the query string.
Solution 31 - C#
The code below is taken off the HttpValueCollection
implementation of ToString
, via ILSpy, which gives you a name=value querystring.
Unfortunately HttpValueCollection is an internal class which you only ever get back if you use HttpUtility.ParseQueryString()
. I removed all the viewstate parts to it, and it encodes by default:
public static class HttpExtensions
{
public static string ToQueryString(this NameValueCollection collection)
{
// This is based off the NameValueCollection.ToString() implementation
int count = collection.Count;
if (count == 0)
return string.Empty;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < count; i++)
{
string text = collection.GetKey(i);
text = HttpUtility.UrlEncodeUnicode(text);
string value = (text != null) ? (text + "=") : string.Empty;
string[] values = collection.GetValues(i);
if (stringBuilder.Length > 0)
{
stringBuilder.Append('&');
}
if (values == null || values.Length == 0)
{
stringBuilder.Append(value);
}
else
{
if (values.Length == 1)
{
stringBuilder.Append(value);
string text2 = values[0];
text2 = HttpUtility.UrlEncodeUnicode(text2);
stringBuilder.Append(text2);
}
else
{
for (int j = 0; j < values.Length; j++)
{
if (j > 0)
{
stringBuilder.Append('&');
}
stringBuilder.Append(value);
string text2 = values[j];
text2 = HttpUtility.UrlEncodeUnicode(text2);
stringBuilder.Append(text2);
}
}
}
}
return stringBuilder.ToString();
}
}
Solution 32 - C#
This is the identical to the accepted answer except slightly more compact:
private string ToQueryString(NameValueCollection nvc)
{
return "?" + string.Join("&", nvc.AllKeys.Select(k => string.Format("{0}={1}",
HttpUtility.UrlEncode(k),
HttpUtility.UrlEncode(nvc[k]))));
}
Solution 33 - C#
Just for those that need the VB.NET version of the top-answer:
Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
Dim array As String() = nvc.AllKeys.SelectMany(Function(key As String) nvc.GetValues(key), Function(key As String, value As String) String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key), System.Web.HttpUtility.UrlEncode(value))).ToArray()
Return "?" + String.Join("&", array)
End Function
And the version without LINQ:
Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
Dim lsParams As New List(Of String)()
For Each strKey As String In nvc.AllKeys
Dim astrValue As String() = nvc.GetValues(strKey)
For Each strValue As String In astrValue
lsParams.Add(String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)))
Next ' Next strValue
Next ' strKey
Dim astrParams As String() = lsParams.ToArray()
lsParams.Clear()
lsParams = Nothing
Return "?" + String.Join("&", astrParams)
End Function ' ToQueryString
And the C# version without LINQ:
public static string ToQueryString(System.Collections.Specialized.NameValueCollection nvc)
{
List<string> lsParams = new List<string>();
foreach (string strKey in nvc.AllKeys)
{
string[] astrValue = nvc.GetValues(strKey);
foreach (string strValue in astrValue)
{
lsParams.Add(string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)));
} // Next strValue
} // Next strKey
string[] astrParams =lsParams.ToArray();
lsParams.Clear();
lsParams = null;
return "?" + string.Join("&", astrParams);
} // End Function ToQueryString
Solution 34 - C#
Works for multiple values per key in NameValueCollection.
ex: { {"k1", "v1"}, {"k1", "v1"} }
=> ?k1=v1&k1=v1
/// <summary>
/// Get query string for name value collection.
/// </summary>
public static string ToQueryString(this NameValueCollection collection,
bool prefixQuestionMark = true)
{
collection.NullArgumentCheck();
if (collection.Keys.Count == 0)
{
return "";
}
var buffer = new StringBuilder();
if (prefixQuestionMark)
{
buffer.Append("?");
}
var append = false;
for (int i = 0; i < collection.Keys.Count; i++)
{
var key = collection.Keys[i];
var values = collection.GetValues(key);
key.NullCheck();
values.NullCheck();
foreach (var value in values)
{
if (append)
{
buffer.Append("&");
}
append = true;
buffer.AppendFormat("{0}={1}", key.UrlEncode(), value.UrlEncode());
}
}
return buffer.ToString();
}
Solution 35 - C#
This is another (maybe redundant :-]) way for do that.
The conceptuals are the same of the Vedran answer in this page (take a look here).
But this class is more efficient, because it iterate through all Keys only one time: when ToString
is invoked.
The formatting code is also semplified and improved.
Hope that could be helpful.
public sealed class QueryStringBuilder
{
public QueryStringBuilder()
{
this.inner = HttpUtility.ParseQueryString(string.Empty);
}
public QueryStringBuilder(string queryString)
{
this.inner = HttpUtility.ParseQueryString(queryString);
}
public QueryStringBuilder(string queryString, Encoding encoding)
{
this.inner = HttpUtility.ParseQueryString(queryString, encoding);
}
private readonly NameValueCollection inner;
public QueryStringBuilder AddKey(string key, string value)
{
this.inner.Add(key, value);
return this;
}
public QueryStringBuilder RemoveKey(string key)
{
this.inner.Remove(key);
return this;
}
public QueryStringBuilder Clear()
{
this.inner.Clear();
return this;
}
public override String ToString()
{
if (this.inner.Count == 0)
return string.Empty;
var builder = new StringBuilder();
for (int i = 0; i < this.inner.Count; i++)
{
if (builder.Length > 0)
builder.Append('&');
var key = this.inner.GetKey(i);
var values = this.inner.GetValues(i);
if (key == null || values == null || values.Length == 0)
continue;
for (int j = 0; j < values.Length; j++)
{
if (j > 0)
builder.Append('&');
builder.Append(HttpUtility.UrlEncode(key));
builder.Append('=');
builder.Append(HttpUtility.UrlEncode(values[j]));
}
}
return builder.ToString();
}
}
Solution 36 - C#
Another approach is to create an extension of the class NameValueCollection that returns the complete Url:
public static class CustomMethods
{
public static string ToUrl(this System.Collections.Specialized.NameValueCollection collection)
{
if (collection.Count == 0) return "";
string completeUrl = "?";
for (int i = 0; i < collection.Count; i++)
{
completeUrl += new Page().Server.UrlEncode(collection.GetKey(i)) + "=" + new Page().Server.UrlEncode(collection.Get(i));
if ((i + 1) < collection.Count) completeUrl += "&";
}
return completeUrl;
}
}
Then, you can use your new method, for example:
System.Collections.Specialized.NameValueCollection qString = new System.Collections.Specialized.NameValueCollection();
qString.Add("name", "MyName");
qString.Add("email", "[email protected]");
qString.ToUrl(); //Result: "?name=MyName&email=myemail%40test.com"
Solution 37 - C#
Here is an implementation which uses very basic language features. It's part of a class which we have to port and maintain in Objective C so we choose to have more lines of code but easier to port and understand by a programmer that isn't very familiar with C#.
/// <summary>
/// Builds a complete http url with query strings.
/// </summary>
/// <param name="pHostname"></param>
/// <param name="pPort"></param>
/// <param name="pPage">ex "/index.html" or index.html</param>
/// <param name="pGetParams">a Dictionary<string,string> collection containing the key value pairs. Pass null if there are none.</param>
/// <returns>a string of the form: http://[pHostname]:[pPort/[pPage]?key1=val1&key2=val2...</returns>
static public string buildURL(string pHostname, int pPort, string pPage, Dictionary<string,string> pGetParams)
{
StringBuilder sb = new StringBuilder(200);
sb.Append("http://");
sb.Append(pHostname);
if( pPort != 80 ) {
sb.Append(pPort);
}
// Allows page param to be passed in with or without leading slash.
if( !pPage.StartsWith("/") ) {
sb.Append("/");
}
sb.Append(pPage);
if (pGetParams != null && pGetParams.Count > 0)
{
sb.Append("?");
foreach (KeyValuePair<string, string> kvp in pGetParams)
{
sb.Append(kvp.Key);
sb.Append("=");
sb.Append( System.Web.HttpUtility.UrlEncode(kvp.Value) );
sb.Append("&");
}
sb.Remove(sb.Length - 1, 1); // Remove the final '&'
}
return sb.ToString();
}
Solution 38 - C#
public string UrlQueryStr(object data)
{
if (data == null)
return string.Empty;
object val;
StringBuilder sb = new StringBuilder();
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(data))
{
if ((val = prop.GetValue(data)) != null)
{
sb.AppendFormat("{0}{1}={2}", sb.Length == 0 ? '?' : '&',
HttpUtility.UrlEncode(prop.Name), HttpUtility.UrlEncode(val.ToString()));
}
}
return sb.ToString();
}
Solution 39 - C#
EDIT - as pointed out in the comments, this is not the way to go.
There is such a class - the URI Class. "Provides an object representation of a uniform resource identifier (URI) and easy access to the parts of the URI." (Microsoft docs).
The following example creates an instance of the Uri class and uses it to create a WebRequest instance.
C# example
Uri siteUri = new Uri("http://www.contoso.com/";);
WebRequest wr = WebRequest.Create(siteUri);
Check it out, there are lots of methods on this class.