459 votos

Cómo construir una cadena de consulta para una dirección URL en C#?

Una tarea común cuando se llama a recursos en la web a partir de un código es la construcción de una cadena de consulta a la inclusión de todos los parámetros necesarios. Mientras que por todos los medios no la ciencia de los cohetes, hay algunos ingeniosos detalles que usted necesita para cuidar de como, anexando un & si no el primer parámetro, la codificación de los parámetros, etc.

El código para hacerlo es muy simple, pero un poco 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")); }
}

Esta es una tarea tan común que uno esperaría de una clase de utilidad para existir, que lo hace más elegante y legible. Escaneo de MSDN, no pude encontrar uno que me lleva a la siguiente pregunta:

¿Cuál es el más elegante y limpia manera de saber que de hacer lo anterior?

658voto

John Bledsoe Puntos 7507

Puede crear una nueva escritura de la instancia de HttpValueCollection llamando System.Web.HttpUtility.ParseQueryString(string.Empty), y luego usarlo como cualquier NameValueCollection. Una vez que haya agregado los valores que usted quiere, usted puede llamar a ToString en la colección para obtener una cadena de consulta, de la siguiente manera:

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

El HttpValueCollection es interno y por eso no puede crear una instancia. Sin embargo, una vez que obtenga un ejemplo, puedes usarlo como cualquier otro NameValueCollection. Dado que el objeto real que está trabajando es un HttpValueCollection, llamando el método ToString de llamar al método reemplazado en HttpValueCollection, que los formatos de la colección como la codificación de dirección URL cadena de consulta.

Después de buscar y por la web de una respuesta a un problema similar, esta es la solución más simple que me podría encontrar.

286voto

annakata Puntos 42676

Si se mira bajo el capó el QueryString de la propiedad es un NameValueCollection. Cuando he hecho cosas similares he sido, en general, interesado en serialising Y deserialising, así que mi sugerencia es crear un NameValueCollection y, a continuación, pasar a:

using System.Web;

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

Posiblemente podría haber formateado que mejor :)

Me imagino que hay un super elegante manera de hacer esto en LINQ demasiado...

88voto

Vedran Puntos 2107

Con la inspiración de Roy Tinker comentario, terminé con un simple método de extensión en la clase Uri que mantiene mi código concisa y limpia:

using System.Web;

public static class HttpExtensions
{
    public static Uri AddQuery(this Uri uri, string name, string value)
    {
        var ub = new UriBuilder(uri);

        // decodes urlencoded pairs from uri.Query to HttpValueCollection
        var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);

        httpValueCollection.Add(name, value);

        // urlencodes the whole HttpValueCollection
        ub.Query = httpValueCollection.ToString();

        return ub.Uri;
    }
}

Uso:

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

Editar - cumple con los Estándares de la variante

Como varias personas se señaló, httpValueCollection.ToString() codifica los caracteres Unicode en un no-compatible con los estándares . Esta es una variante del mismo método de extensión que se encarga de dichos personajes invocando HttpUtility.UrlEncode método en lugar del obsoleto HttpUtility.UrlEncodeUnicode método.

using System.Web;

public static Uri AddQuery(this Uri uri, string name, string value)
{
    var ub = new UriBuilder(uri);

    // decodes urlencoded pairs from uri.Query to HttpValueCollection
    var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);
    httpValueCollection.Add(name, value);

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

29voto

Igal Tabachnik Puntos 15160

Me contestó una pregunta similar hace un tiempo. Básicamente, la mejor manera sería utilizar la clase HttpValueCollection, que de ASP.NET Request.QueryString de la propiedad en realidad es, por desgracia, se interna en el .NET marco. Usted podría utilizar un Reflector, para apoderarse de ella (y lo ponga en su clase Utils). De esta forma se podría manipular la cadena de consulta como un NameValueCollection, pero con todas las url de codificación/decodificación de problemas de cuidado para usted.

HttpValueCollection extends NameValueCollection, y tiene un constructor que toma un codificada de la cadena de consulta (signos y signos de interrogación incluido), y se anula una ToString() método para posteriormente reconstruir la cadena de consulta de la subyacentes de la colección.

Ejemplo:

  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

28voto

Alfred Puntos 505

He aquí un fluido/lambda-ish manera como un método de extensión (combinación de los conceptos en posts anteriores), que soporta múltiples valores para la misma clave. Mi preferencia personal es extensiones de contenedores para descubrir-capacidad por otros miembros del equipo para cosas como esta. Tenga en cuenta que hay controversia en torno a métodos de codificación, un montón de posts sobre esto en Stack Overflow (uno de esos post) y MSDN bloggers (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: con nulo apoyo, aunque probablemente tendrá que adaptar a su situación 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;
    }

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X