Cómo analizar una cadena de consulta en una NameValueCollection en .NET

186

Me gustaría analizar una cadena como p1=6&p2=7&p3=8en un NameValueCollection.

¿Cuál es la forma más elegante de hacer esto cuando no tienes acceso al Page.Requestobjeto?

Nathan Smith
fuente

Respuestas:

356

Hay una utilidad .NET incorporada para esto: HttpUtility.ParseQueryString

// C#
NameValueCollection qscoll = HttpUtility.ParseQueryString(querystring);
' VB.NET
Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)

Es posible que deba reemplazar querystringcon new Uri(fullUrl).Query.

Guy Starbuck
fuente
26
Omar, no me funcionó en ASP.NET 4, me devolvió una clave de " stackoverflow.com?para " en lugar de "para". Así que estoy usando HttpUtility.ParseQueryString (nuevo Uri (fullUrl) .Query) que funciona correctamente para mí.
Michael
2
qscoll ["p1"], qscoll ["p2"] y qscoll ["p3"]
SMUsamaShah
55
ParseQueryString es realmente una mala idea para usar en una aplicación de escritorio, porque no está incluida en el Perfil del Cliente; ¿Por qué instalar 100 M de bibliotecas adicionales en la computadora cliente para usar un método simple? Sin embargo, parece que Microsoft no tiene una mejor idea. La única forma es implementar un método propio o usar una implementación de código abierto.
Vitalii
2
VASoftOnline: en ese caso, puede usar la implementación Mono de ParseQueryString: github.com/mono/mono/blob/master/mcs/class/System.Web/… la licencia para eso es MIT X11: github.com/mono/mono / blob / master / LICENCIA
sw.
3
HttpUtility.ParseQueryStringsería mi recomendación, excepto que en el caso de que HttpUtility.ParseQueryString("&X=1&X=2&X=3")el resultado sea ....X=1,2,3...tener múltiples parámetros del mismo nombre es poco común pero necesario para admitir parámetros de controlador como int [], IEnumerable <int>, etc. (dichos parámetros podrían utilizarse para admitir varias casillas de verificación) consulte "Múltiples ocurrencias de la misma variable de cadena de consulta se consolidan en una entrada" según el sitio de MS . Una versión artesanal del método podría ser su única opción
dunxz
43

HttpUtility.ParseQueryString funcionará mientras esté en una aplicación web o no le importe incluir una dependencia en System.Web. Otra forma de hacer esto es:

NameValueCollection queryParameters = new NameValueCollection();
string[] querySegments = queryString.Split('&');
foreach(string segment in querySegments)
{
   string[] parts = segment.Split('=');
   if (parts.Length > 0)
   {
      string key = parts[0].Trim(new char[] { '?', ' ' });
      string val = parts[1].Trim();

      queryParameters.Add(key, val);
   }
}
Scott Dorman
fuente
55
debe verificar si las partes. Longitud> 1, para asegurarse de que puede llamar a las partes [1]
Alexandru Pupsa
2
¿Qué pasa si no hay valor? Por ejemplo, una cadena de consulta puede verse ?foo=1&bar. HttpUtilitylo analizaría como{ key = null, value = "bar" }
Thomas Levesque
Esto es excelente en comparación con HttpUtility.ParseQueryString porque no decodifica los valores (por lo que se conservan los valores codificados en base64)
CRice
1
En realidad, aún debe permitir que el valor '=', por ejemplo & code = A0SYw34Hj / m ++ lH0s7r0l / yg6GWdymzSCbI2zOn3V4o = se elimine el último carácter '='. Reescribí parte de su código para obtener el primer índice de '=' y la subcadena de la clave y el valor para arreglar esto (en lugar de usar la división).
CRice
28

Muchas de las respuestas proporcionan ejemplos personalizados debido a la dependencia de la respuesta aceptada en System.Web . Desde el paquete Microsoft.AspNet.WebApi.Client NuGet hay un método UriExtensions.ParseQueryString , que también se puede usar:

var uri = new Uri("https://stackoverflow.com/a/22167748?p1=6&p2=7&p3=8");
NameValueCollection query = uri.ParseQueryString();

Entonces, si desea evitar la dependencia de System.Web y no quiere usar la suya propia, esta es una buena opción.

James Skimming
fuente
3
La misma función existe como una extensión en el espacio de nombres System.Net.Http (vea mi respuesta a continuación), no necesita otra dependencia completa ...
jvenema
@jvenema Desde dónde agrega la dependencia System.Net.Http.Formatting, creo que solo se proporciona agregando el paquete NuGet Microsoft.AspNet.WebApi.Client.
James Skimming
19

Quería eliminar la dependencia de System.Web para poder analizar la cadena de consulta de una implementación de ClickOnce, al tiempo que los requisitos previos se limitaban al "Subconjunto de marcos solo para clientes".

Me gustó la respuesta de rp. Agregué algo de lógica adicional.

public static NameValueCollection ParseQueryString(string s)
    {
        NameValueCollection nvc = new NameValueCollection();

        // remove anything other than query string from url
        if(s.Contains("?"))
        {
            s = s.Substring(s.IndexOf('?') + 1);
        }

        foreach (string vp in Regex.Split(s, "&"))
        {
            string[] singlePair = Regex.Split(vp, "=");
            if (singlePair.Length == 2)
            {
                nvc.Add(singlePair[0], singlePair[1]);
            }
            else
            {
                // only one key with no value specified in query string
                nvc.Add(singlePair[0], string.Empty);
            }
        }

        return nvc;
    }
densom
fuente
Esto es realmente útil para Windows Phone, solo tiene que reemplazar el "NameValueCollection" con un "SortedDictionnary <string, string>"
Mike Bryant
44
Tenga cuidado al cambiar NameValueCollection por un diccionario: ¡no son lo mismo! Las cadenas de consulta admiten varias claves con el mismo valor, y también lo hace NameValueCollection.
Matt DeKrey
7

Necesitaba una función que sea un poco más versátil que la que ya se proporcionaba al trabajar con consultas OLSC.

  • Los valores pueden contener múltiples signos iguales
  • Decodificar caracteres codificados en nombre y valor
  • Capaz de ejecutarse en Client Framework
  • Capaz de ejecutarse en Mobile Framework.

Aquí está mi solución:

Public Shared Function ParseQueryString(ByVal uri As Uri) As System.Collections.Specialized.NameValueCollection
    Dim result = New System.Collections.Specialized.NameValueCollection(4)
    Dim query = uri.Query
    If Not String.IsNullOrEmpty(query) Then
        Dim pairs = query.Substring(1).Split("&"c)
        For Each pair In pairs
            Dim parts = pair.Split({"="c}, 2)

            Dim name = System.Uri.UnescapeDataString(parts(0))
            Dim value = If(parts.Length = 1, String.Empty,
                System.Uri.UnescapeDataString(parts(1)))

            result.Add(name, value)
        Next
    End If
    Return result
End Function

Puede que no sea una mala idea <Extension()>agregar eso también para agregar la capacidad a Uri.

Josh Brown
fuente
7

Para hacerlo sin System.Web, sin escribirlo usted mismo, y sin paquetes NuGet adicionales:

  1. Agregar una referencia a System.Net.Http.Formatting
  2. Añadir using System.Net.Http;
  3. Usa este código:

    new Uri(uri).ParseQueryString()

https://msdn.microsoft.com/en-us/library/system.net.http.uriextensions(v=vs.118).aspx

jvenema
fuente
3
Desde dónde está agregando la dependencia System.Net.Http.Formatting, creo que solo se proporciona agregando el paquete Microsoft.AspNet.WebApi.Client NuGet.
James Skimming
Huh, mi mal. Supongo que vino con el marco MVC que se instala automáticamente, por lo que no tuve que agregar ningún paquete adicional (Archivos de programa (x86) \ Microsoft ASP.NET \ ASP.NET MVC 4 \ Assemblies \ System.Net. Http.Formatting.dll)
jvenema
System.Net.Formatting EXTENSION VS2019 es una lista o pestaña separada de las referencias del marco tradicional
Sql Surfer
3

Si desea evitar la dependencia de System.Web que se requiere para usar HttpUtility.ParseQueryString , puede usar el Urimétodo de extensión que se ParseQueryStringencuentra en System.Net.Http.

Asegúrese de agregar una referencia (si aún no lo ha hecho) a System.Net.Httpsu proyecto.

Tenga en cuenta que debe convertir el cuerpo de la respuesta a válido Uripara que ParseQueryString(in System.Net.Http) funcione.

string body = "value1=randomvalue1&value2=randomValue2";

// "http://localhost/query?" is added to the string "body" in order to create a valid Uri.
string urlBody = "http://localhost/query?" + body;
NameValueCollection coll = new Uri(urlBody).ParseQueryString();
Amadeus Sánchez
fuente
System.Net.Formatting EXTENSION VS2019 coloca las referencias de extensión separadas de las referencias de framework tradicionales.
Sql Surfer
2
    private void button1_Click( object sender, EventArgs e )
    {
        string s = @"p1=6&p2=7&p3=8";
        NameValueCollection nvc = new NameValueCollection();

        foreach ( string vp in Regex.Split( s, "&" ) )
        {
            string[] singlePair = Regex.Split( vp, "=" );
            if ( singlePair.Length == 2 )
            {
                nvc.Add( singlePair[ 0 ], singlePair[ 1 ] );    
            }    
        }
    }
rp.
fuente
55
también se permite el punto y coma como separador de parámetros en http, mejor no reinventar la rueda
Matthew Lock
2

Me acabo de dar cuenta de que Web API Client tiene un ParseQueryStringmétodo de extensión que funciona en Uriay devuelve un HttpValueCollection:

var parameters = uri.ParseQueryString();
string foo = parameters["foo"];
Thomas Levesque
fuente
1

Simplemente acceda a Request.QueryString. AllKeys mencionado como otra respuesta solo te da una variedad de claves.

Sabueso
fuente
1

HttpUtility.ParseQueryString(Request.Url.Query)return es HttpValueCollection(clase interna). Hereda de NameValueCollection.

    var qs = HttpUtility.ParseQueryString(Request.Url.Query);
    qs.Remove("foo"); 

    string url = "~/Default.aspx"; 
    if (qs.Count > 0)
       url = url + "?" + qs.ToString();

    Response.Redirect(url); 
alex1kirch
fuente
1

Dado que todo el mundo parece estar pegando su solución ... aquí está la mía :-) Necesitaba esto desde una biblioteca de clase sin System.Webobtener los parámetros de identificación de los hipervínculos almacenados.

Pensé en compartir porque encuentro esta solución más rápida y mejor.

public static class Statics
    public static Dictionary<string, string> QueryParse(string url)
    {
        Dictionary<string, string> qDict = new Dictionary<string, string>();
        foreach (string qPair in url.Substring(url.IndexOf('?') + 1).Split('&'))
        {
            string[] qVal = qPair.Split('=');
            qDict.Add(qVal[0], Uri.UnescapeDataString(qVal[1]));
        }
        return qDict;
    }

    public static string QueryGet(string url, string param)
    {
        var qDict = QueryParse(url);
        return qDict[param];
    }
}

Uso:

Statics.QueryGet(url, "id")
Tiele Declercq
fuente
1
Solo un problema con este método: una cadena de consulta puede tener más de un valor para un parámetro dado. En este caso, su método arrojará un error de clave duplicada.
Thomas Levesque
Sí, al menos use una NameValueCollection, las cadenas de consulta con claves duplicadas son perfectamente legales.
Chad Grant
Además, su diccionario distingue entre mayúsculas y minúsculas, por lo que X = 1 yx = 1 serían claves diferentes. Necesita un nuevo diccionario <string, string> (StringComparer.OrdinalIgnoreCase);
Chad Grant
0

Acceda a Request.QueryString.Keys para una NameValueCollection de todos los parámetros de cadena de consulta.

Mark Glorie
fuente
0

Para obtener todos los valores de Querystring intente esto:

    Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)

Dim sb As New StringBuilder("<br />")
For Each s As String In qscoll.AllKeys

  Response.Write(s & " - " & qscoll(s) & "<br />")

Next s
mirko cro 1234
fuente
0
        var q = Request.QueryString;
        NameValueCollection qscoll = HttpUtility.ParseQueryString(q.ToString());
Hamit YILDIRIM
fuente
0
let search = window.location.search;

console.log(search);

let qString = search.substring(1);

while(qString.indexOf("+") !== -1)

   qString  = qString.replace("+", "");

let qArray = qString.split("&");

let values = [];

for(let i = 0; i < qArray.length; i++){
   let pos = qArray[i].search("=");
   let keyVal = qArray[i].substring(0, pos);
   let dataVal = qArray[i].substring(pos + 1);
   dataVal = decodeURIComponent(dataVal);
   values[keyVal] = dataVal;
}
Nahom Haile
fuente
-1

Traduzco a la versión C # de josh-brown en VB

private System.Collections.Specialized.NameValueCollection ParseQueryString(Uri uri)
{
    var result = new System.Collections.Specialized.NameValueCollection(4);
    var query = uri.Query;
    if (!String.IsNullOrEmpty(query))
    {
        var pairs = query.Substring(1).Split("&".ToCharArray());
        foreach (var pair in pairs)
        {
            var parts = pair.Split("=".ToCharArray(), 2);
            var name = System.Uri.UnescapeDataString(parts[0]);
            var value = (parts.Length == 1) ? String.Empty : System.Uri.UnescapeDataString(parts[1]);
            result.Add(name, value);
        }
    }
    return result;
}
elgoya
fuente
-2

Este es mi código, creo que es muy útil:

public String GetQueryString(string ItemToRemoveOrInsert = null, string InsertValue = null )
{
    System.Collections.Specialized.NameValueCollection filtered = new System.Collections.Specialized.NameValueCollection(Request.QueryString);
    if (ItemToRemoveOrInsert != null)
    {
        filtered.Remove(ItemToRemoveOrInsert);
        if (!string.IsNullOrWhiteSpace(InsertValue))
        {
            filtered.Add(ItemToRemoveOrInsert, InsertValue);
        }
    }

    string StrQr = string.Join("&", filtered.AllKeys.Select(key => key + "=" + filtered[key]).ToArray());
    if (!string.IsNullOrWhiteSpace(StrQr)){
        StrQr="?" + StrQr;
    }

    return StrQr;
}
Farhawd
fuente