Como construir uma seqüência de consulta para uma URL em c #?

Uma tarefa comum ao chamar resources da web de um código é criar uma string de consulta para include todos os parâmetros necessários. Embora, por todos os meios, não haja ciência de foguetes, há alguns detalhes bacanas que você precisa cuidar, como acrescentar um & se não o primeiro parâmetro, codificar os parâmetros etc.

O código para fazer isso é muito simples, mas um pouco tedioso:

 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")); } } 

Essa é uma tarefa tão comum que se espera que exista uma class de utilitários que a torne mais elegante e legível. Analisando o MSDN, não consegui encontrar um – o que me leva à seguinte pergunta:

Qual é a maneira mais elegante e limpa que você conhece de fazer o acima?

Se você olhar sob o capô a propriedade QueryString é um NameValueCollection. Quando eu fiz coisas parecidas, geralmente tenho interesse em serializar e deserializar, então minha sugestão é criar um NameValueCollection e passar para:

 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); } 

Possivelmente eu poderia ter formatado melhor 🙂

Eu imagino que há uma maneira super elegante de fazer isso no LINQ também …

Você pode criar uma nova instância gravável de HttpValueCollection chamando System.Web.HttpUtility.ParseQueryString(string.Empty) e, em seguida, usá-lo como qualquer NameValueCollection . Depois de adicionar os valores desejados, você pode chamar ToString na coleção para obter uma string de consulta, da seguinte maneira:

 NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty); queryString["key1"] = "value1"; queryString["key2"] = "value2"; return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded 

O HttpValueCollection é interno e, portanto, você não pode construir diretamente uma instância. No entanto, depois de obter uma instância, você poderá usá-la como qualquer outra NameValueCollection . Como o object real com o qual você está trabalhando é um HttpValueCollection , chamar o método ToString chamará o método substituído em HttpValueCollection , que formata a coleção como uma string de consulta codificada por URL.

Depois de pesquisar SO e na Web por uma resposta a um problema semelhante, esta é a solução mais simples que pude encontrar.

.NET Core

Se você estiver trabalhando no .NET Core, poderá usar a class Microsoft.AspNetCore.WebUtilities.QueryHelpers , o que simplifica bastante isso.

https://docs.microsoft.com/pt-br/aspnet/core/api/microsoft.aspnetcore.webutilities.queryhelpers

Com a inspiração do comentário de Roy Tinker, acabei usando um método de extensão simples na class Uri que mantém meu código conciso e limpo:

 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; } } 

Uso:

 Uri url = new Uri("http://localhost/rest/something/browse"). AddQuery("page", "0"). AddQuery("pageSize", "200"); 

Editar – variante compatível com padrões

Como várias pessoas apontaram, httpValueCollection.ToString() codifica caracteres Unicode de maneira não compatível com os padrões . Essa é uma variante do mesmo método de extensão que manipula esses caracteres invocando o método HttpUtility.UrlEncode vez do método HttpUtility.UrlEncodeUnicode obsoleto.

 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; } 

Eu respondi a uma pergunta semelhante há um tempo atrás. Basicamente, a melhor maneira seria usar a class HttpValueCollection , que a propriedade Request.QueryString do ASP.NET, na verdade, é, infelizmente, é interna no .NET framework. Você poderia usar o Reflector para pegá-lo (e colocá-lo na sua class Utils). Dessa forma, você poderia manipular a string de consulta como um NameValueCollection, mas com todos os problemas de codificação / decodificação de url atendidos.

HttpValueCollection estende NameValueCollection e tem um construtor que leva uma seqüência de consulta codificada (E comercial e pontos de interrogação incluídos) e substitui um método ToString() para reconstruir posteriormente a seqüência de consulta da coleção subjacente.

Exemplo:

  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&paramA=A&paramB=B 

Aqui está uma maneira fluente / lambda-como um método de extensão (combinando conceitos em postagens anteriores) que suporta múltiplos valores para a mesma chave. Minha preferência pessoal é extensões sobre invólucros para capacidade de descoberta por outros membros da equipe para coisas como essa. Observe que há controvérsias em torno dos methods de codificação, muitos posts sobre isso no Stack Overflow (um desses posts ) e blogueiros do MSDN (como este ).

 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: com suporte nulo, embora você provavelmente precise adaptá-lo para sua situação particular

 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; } 

Flurl [divulgação: eu sou o autor] suporta a construção de strings de consulta através de objects anônimos (entre outras maneiras):

 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!" }); 

A biblioteca complementar Flurl.Http opcional permite que você faça chamadas HTTP diretamente da mesma cadeia de chamadas fluente, estendendo-a para um cliente REST completo:

 await "https://api.mysite.com" .AppendPathSegment("person") .SetQueryParams(new { ap_key = "my-key" }) .WithOAuthBearerToken("MyToken") .PostJsonAsync(new { first_name = firstName, last_name = lastName }); 

O pacote completo está disponível no NuGet:

PM> Install-Package Flurl.Http

ou apenas o construtor de URL independente:

PM> Install-Package Flurl

Aqui está minha input tardia. Eu não gostava de nenhum dos outros por várias razões, então eu escrevi o meu próprio.

Esta versão apresenta:

  • Uso apenas do StringBuilder. Nenhuma chamada ToArray () ou outros methods de extensão. Ele não parece tão bonito quanto algumas das outras respostas, mas eu considero isso uma function central, então a eficiência é mais importante do que ter um código “fluente”, “de uma linha” que esconde ineficiências.

  • Lida com vários valores por chave. (Não precisei de mim, mas apenas para silenciar 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(); } 

Exemplo de uso

  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); 

Saída

 http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26 

Que tal criar methods de extensão que lhe permitam adicionar os parâmetros em um estilo fluente como este?

 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(); 

Aqui está a sobrecarga que usa uma 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); } 

E aqui está a sobrecarga que usa um 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)); } 

Eu precisava resolver o mesmo problema para uma biblioteca de classs portátil (PCL) na qual estou trabalhando. Nesse caso, não tenho access ao System.Web, portanto, não posso usar ParseQueryString.

Em vez disso, usei System.Net.Http.FormUrlEncodedContent assim:

 var url = new UriBuilder("http://example.com"); url.Query = new FormUrlEncodedContent(new Dictionary() { {"param1", "val1"}, {"param2", "val2"}, {"param3", "val3"}, }).ReadAsStringAsync().Result; 
  public static string ToQueryString(this Dictionary 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().Select(key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(source[key]))).ToArray()); } 

Minha oferta:

 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; } 

Uso:

 var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method") .AddQuery("wow", "soFluent"); // http://stackoverflow.com?such=method&wow=soFluent 

Adicione esta class ao seu projeto

 using System; using System.Collections.Generic; using System.Linq; using System.Web; public class QueryStringBuilder { private readonly List> _list; public QueryStringBuilder() { _list = new List>(); } public void Add(string name, object value) { _list.Add(new KeyValuePair(name, value)); } public override string ToString() { return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key), "=", Uri.EscapeDataString(kvp.Value.ToString())))); } } 

E usá-lo assim:

 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" 

Não testado, mas acho que algo nesse sentido funcionaria muito bem

 public class QueryString { private Dictionary _Params = new Dictionary(); public overide ToString() { List returnParams = new List(); 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(); 

Uma versão baseada no método de extensão rápida:

 class Program { static void Main(string[] args) { var parameters = new List> { new KeyValuePair("A", "AValue"), new KeyValuePair("B", "BValue") }; string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray()); } } public static class KeyValueExtensions { public static string ToQueryString(this KeyValuePair obj) { return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value); } } 

Você poderia usar uma cláusula where para selecionar quais parâmetros são adicionados à string.

Supondo que você deseja reduzir as dependencies para outros assemblies e manter as coisas simples, você pode fazer:

 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(); 

Isso funciona bem com loops também. A remoção final de e comercial precisa sair do loop.

Observe que o operador de concatenação é usado para melhorar a legibilidade. O custo de usá-lo em comparação com o custo de usar um StringBuilder é mínimo (acho que Jeff Atwood postou algo sobre esse tópico).

[Também input tardia]

Classe de invólucro capaz de cadeia para 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(); } } } 

Exemplo de uso:

 QueryStringBuilder parameters = new QueryStringBuilder() .Add("view", ViewBag.PageView) .Add("page", ViewBag.PageNumber) .Add("size", ViewBag.PageSize); string queryString = parameters.ToString(); 

Igual à solução aceita, mas transcrita para “pontilhar” a syntax LINQ …

 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; } 

Combinou as respostas principais para criar uma versão de object anônima :

 var queryString = HttpUtility2.BuildQueryString(new { key2 = "value2", key1 = "value1", }); 

Isso gera isso:

key2 = value2 & key1 = value1

Aqui está o código:

 public static class HttpUtility2 { public static string BuildQueryString(T obj) { var queryString = HttpUtility.ParseQueryString(string.Empty); foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast()) { var value = (property.GetValue(obj) ?? "").ToString(); queryString.Add(property.Name, value); } return queryString.ToString(); } } 

Eu tenho um método de extensão para Uri que:

  • Aceita objects anônimos: uri.WithQuery(new { name = "value" })
  • Aceita collections de pares string/string (por exemplo, Dictionary`2 ).
  • Aceita collections de pares string/object (por exemplo, RouteValueDictionary ).
  • Aceita NameValueCollection s.
  • Classifica os valores da consulta por chave para que os mesmos valores produzam URIs iguais.
  • Suporta vários valores por chave, preservando sua ordem original.

A versão documentada pode ser encontrada aqui .

A extensão:

 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; } 

O analisador de consulta:

 private static IEnumerable> 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(key, val); // Check if a string/string dictionary. var ssd = values as IEnumerable>; if (ssd != null) return ssd; // Check if a string/object dictionary. var sod = values as IEnumerable>; if (sod == null) { // Check if a non-generic dictionary. var ngd = values as IDictionary; if (ngd != null) sod = ngd.Cast().ToDictionary( 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 ?? new[] { pair.Value?.ToString() } select new KeyValuePair(pair.Key, val); } 

Aqui estão os testes:

 var uri = new Uri("https://stackoverflow.com/yo?oldKey=oldValue"); // Test with a string/string dictionary. var q = uri.WithQuery(new Dictionary { ["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 { ["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> { [1] = new HashSet { "v1" }, [2] = new HashSet { "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 { "v3" }, k4 = true, k5 = null as Queue }); 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")); 

Eu adicionei o seguinte método à minha class PageBase.

 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()); } 

Chamar:

 NameValueCollection querystrings = new NameValueCollection(); querystrings.Add("language", "en"); querystrings.Add("id", "134"); this.Redirect("http://www.mypage.com", querystrings); 

Eu escrevi alguns methods de extensão que eu achei muito úteis ao trabalhar com QueryStrings. Muitas vezes eu quero começar com o atual QueryString e modificar antes de usá-lo. Algo como,

 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 

Para mais e a fonte: http://www.charlesrcook.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx

É básico, mas gosto do estilo.

Só queria jogar meus 2 centavos:

 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 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[] 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 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)))); } } 

Os docs dizem que uri.Query vai começar com um ? se não estiver vazio e você deve cortá-lo se for modificá-lo.

Observe que HttpUtility.UrlEncode é encontrado no System.Web .

Uso:

 var uri = new Uri("https://api.del.icio.us/v1/posts/suggest").AddQueryParam("url","http://stackoverflow.com") 
 // 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")); } 

Saída:

http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue

O código; todos vocês podem me agradecer em algum lugar, de alguma forma: 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 Paths { get; set; } public SortedDictionary QueryPairs { get; set; } public UrlBuilder(string url) { this.Paths = new List(); this.QueryPairs = new SortedDictionary(); 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 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); } } } 

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 QueryParametersDictionary() { var dictionary = new Dictionary(); 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 nvc) { StringBuilder sb = new StringBuilder(); bool first = true; foreach (KeyValuePair pair in nvc) { if (!first) { sb.Append("&"); } sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value)); first = false; } return sb.ToString(); } 

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 = '?@Helpers.GetQueryStringWithValue("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.

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(); } } 

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])))); } 

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 lsParams = new List(); 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 

Works for multiple values per key in NameValueCollection.

ex: { {"k1", "v1"}, {"k1", "v1"} } => ?k1=v1&k1=v1

 ///  /// Get query string for name value collection. ///  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(); } 

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(); } }