Como analisar uma seqüência de caracteres de consulta em um NameValueCollection no .NET

186

Eu gostaria de analisar uma string como p1=6&p2=7&p3=8em a NameValueCollection.

Qual é a maneira mais elegante de fazer isso quando você não tem acesso ao Page.Requestobjeto?

Nathan Smith
fonte

Respostas:

356

Há um utilitário .NET interno para isso: HttpUtility.ParseQueryString

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

Pode ser necessário substituir querystringpor new Uri(fullUrl).Query.

Guy Starbuck
fonte
26
Omar, não funcionou para mim no ASP.NET 4, retornou uma chave de " stackoverflow.com?para " em vez de "para". Então, eu estou usando HttpUtility.ParseQueryString (novo Uri (fullUrl) .Query), que funciona corretamente para mim.
Michael
2
qscoll ["p1"], qscoll ["p2"] e qscoll ["p3"]
SMUsamaShah 01/09/11
5
ParseQueryString é realmente uma péssima idéia para uso em aplicativos de desktop, porque não está incluído no perfil do cliente; por que instalar 100 M de bibliotecas adicionais no computador cliente para usar apenas um método simples? No entanto, parece que a Microsoft não tem nenhuma idéia melhor. A única maneira é implementar o próprio método ou usar a implementação de código aberto.
Vitalii
2
VASoftOnline: Nesse caso, você pode usar a implementação Mono do ParseQueryString: github.com/mono/mono/blob/master/mcs/class/System.Web/… a licença para isso é MIT X11: github.com/mono/mono / blob / master / LICENSE
sw.
3
HttpUtility.ParseQueryStringseria minha recomendação, exceto que, no caso do HttpUtility.ParseQueryString("&X=1&X=2&X=3")resultado, ....X=1,2,3...Ter vários parâmetros do mesmo nome é incomum, mas necessário para suportar parâmetros do controlador como int [], IEnumerable <int> etc. (esses parâmetros podem ser usados ​​para suportar várias caixas de seleção) consulte "Várias ocorrências da mesma variável de cadeia de consulta são consolidadas em uma entrada", conforme o site da MS . Uma versão artesanal do método pode ser sua única opção
dunxz
43

O HttpUtility.ParseQueryString funcionará desde que você esteja em um aplicativo Web ou não se importe de incluir uma dependência no System.Web. Outra maneira de fazer isso é:

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
fonte
5
você deve verificar se parts.Length> 1, para ter certeza de que você pode chamar partes [1]
Alexandru Pupsa
2
E se não houver valor? por exemplo, uma string de consulta pode parecer ?foo=1&bar. HttpUtilityanalisaria como{ key = null, value = "bar" }
Thomas Levesque
Este é grande em comparação com HttpUtility.ParseQueryString porque não descodificar os valores (de modo base64 valores codificados são preservados)
CRice
1
Na verdade, você ainda precisa permitir que o valor '=', por exemplo, & code = A0SYw34Hj / m ++ lH0s7r0l / yg6GWdymzSCbI2zOn3V4o = tenha o último caractere '=' removido. Reescrevi parte do seu código para obter o primeiro índice de '=' e substring a chave e o valor para corrigir isso (em vez de usar split).
CR7 07/07
28

Muitas das respostas estão fornecendo exemplos personalizados devido à dependência da resposta aceita no System.Web . Do Microsoft.AspNet.WebApi.Client pacote NuGet há uma UriExtensions.ParseQueryString , método que também pode ser usado:

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

Portanto, se você deseja evitar a dependência System.Web e não deseja criar sua própria, essa é uma boa opção.

James Skimming
fonte
3
A mesma função existe como uma extensão no namespace System.Net.Http (ver minha resposta abaixo), não há necessidade para toda outra dependência ...
jvenema
@jvenema De onde você está adicionando a dependência System.Net.Http.Formatting, acredito que ela seja fornecida apenas pela adição do pacote Microsoft.AspNet.WebApi.Client NuGet.
21418 James Skimming #
19

Eu queria remover a dependência do System.Web para poder analisar a sequência de consultas de uma implantação do ClickOnce, mantendo os pré-requisitos limitados ao "Subconjunto da estrutura do cliente apenas".

Eu gostei da resposta do rp. Eu adicionei alguma 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
fonte
isso é realmente útil para Windows Phone, você só tem que substituir o "NameValueCollection" com um "SortedDictionnary <string, string>"
Mike Bryant
4
Cuidado ao alternar NameValueCollection para um dicionário - eles não são os mesmos! As cadeias de consulta oferecem suporte a várias chaves com o mesmo valor, assim como o NameValueCollection.
precisa saber é o seguinte
7

Eu precisava de uma função que seja um pouco mais versátil do que a que já foi fornecida ao trabalhar com consultas OLSC.

  • Os valores podem conter vários sinais de igual
  • Decodificar caracteres codificados em nome e valor
  • Capaz de executar no Client Framework
  • Capaz de executar no Mobile Framework.

Aqui está a minha solução:

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

Pode não ser uma má idéia abordar <Extension()>isso também para adicionar a capacidade ao próprio Uri.

Josh Brown
fonte
7

Para fazer isso sem System.Web, sem você mesmo escrever e sem pacotes NuGet adicionais:

  1. Adicione uma referência a System.Net.Http.Formatting
  2. Adicionar using System.Net.Http;
  3. Use este código:

    new Uri(uri).ParseQueryString()

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

jvenema
fonte
3
De onde você está adicionando a dependência System.Net.Http.Formatting, acredito que ela é fornecida apenas pela adição do pacote Microsoft.AspNet.WebApi.Client NuGet.
21418 James Skimming #
Huh, meu mal. Eu acho que ele veio com a estrutura MVC que é instalada automaticamente, então não precisei adicionar nenhum pacote adicional (Arquivos de Programas (x86) \ Microsoft ASP.NET \ ASP.NET MVC 4 \ Assemblies \ System.Net. Http.Formatting.dll)
jvenema
System.Net.Formatting EXTENSÃO VS2019 é uma lista separada ou aba do a partir de referências estruturais tradicionais
Sql Surfer
3

Se você quiser evitar a dependência de System.Web que é necessário para usar HttpUtility.ParseQueryString , você poderia usar o Urimétodo de extensão ParseQueryStringencontrada em System.Net.Http.

Certifique-se de adicionar uma referência (se ainda não o fez) ao System.Net.Httpseu projeto.

Observe que você precisa converter o corpo da resposta em um 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
fonte
O System.Net.Formatting EXTENSION VS2019 coloca referências de extensão separadas das referências de estrutura tradicionais.
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.
fonte
5
ponto e vírgula também é permitido como um separador de parâmetro no http, melhor não reinventar a roda
Matthew Bloqueio
2

Acabei de perceber que o Web API Client tem um ParseQueryStringmétodo de extensão que funciona em um Urie retorna um HttpValueCollection:

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

Basta acessar Request.QueryString. AllKeys mencionadas como outra resposta apenas fornece uma variedade de chaves.

Bloodhound
fonte
1

HttpUtility.ParseQueryString(Request.Url.Query)return é HttpValueCollection(classe interna). Herda 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
fonte
1

Se você não deseja a dependência System.Web, basta colar este código-fonte da classe HttpUtility.

Acabei de juntar isso a partir do código fonte do Mono . Ele contém o HttpUtility e todas as suas dependências (como IHtmlString, Helpers, HttpEncoder, HttpQSCollection).

Então use HttpUtility.ParseQueryString.

https://gist.github.com/bjorn-ali-goransson/b04a7c44808bb2de8cca3fc9a3762f9c


fonte
1

Como todo mundo parece estar colando sua solução ... aqui está a minha :-) Eu precisava disso dentro de uma biblioteca de classes sem System.Webbuscar parâmetros de identificação dos hiperlinks armazenados.

Pensei em compartilhar porque acho essa solução mais rápida e com melhor aparência.

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
fonte
1
Apenas um problema com este método: uma string de consulta pode ter mais de um valor para um determinado parâmetro. Nesse caso, seu método gerará um erro de chave duplicada.
Thomas Levesque
sim, pelo menos use um NameValueCollection, as strings de consulta com chaves duplicadas são perfeitamente legais.
Chad Grant
Além disso, seu dicionário faz distinção entre maiúsculas e minúsculas; portanto, X = 1 e x = 1 seriam chaves diferentes. Precisa de um novo Dicionário <string, string> (StringComparer.OrdinalIgnoreCase);
Chad Grant
0

Ative Request.QueryString.Keys para um NameValueCollection de todos os parâmetros de seqüência de caracteres de consulta.

Mark Glorie
fonte
0

Para obter todos os valores de consulta, tente o seguinte:

    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
fonte
0
        var q = Request.QueryString;
        NameValueCollection qscoll = HttpUtility.ParseQueryString(q.ToString());
Hamit YILDIRIM
fonte
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
fonte
-1

Eu traduzo para a versão c # de josh-brown no 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
fonte
-2

Este é o meu código, acho muito ú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
fonte