Jak parsować ciąg zapytania w NameValueCollection w .NET

186

Chciałbym parsować ciąg taki jak p1=6&p2=7&p3=8na NameValueCollection.

Jaki jest najbardziej elegancki sposób, gdy nie masz dostępu do Page.Requestobiektu?

Nathan Smith
źródło

Odpowiedzi:

356

Do tego celu służy wbudowane narzędzie .NET: HttpUtility.ParseQueryString

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

Być może trzeba wymienić querystringz new Uri(fullUrl).Query.

Guy Starbuck
źródło
26
Omar, nie działało to dla mnie na ASP.NET 4, zwróciło klucz „ stackoverflow.com?para ” zamiast „para”. Więc używam HttpUtility.ParseQueryString (nowy Uri (fullUrl) .Query), który działa poprawnie dla mnie.
Michael
2
qscoll [„p1”], qscoll [„p2”] i qscoll [„p3”]
SMUsamaShah
5
ParseQueryString jest naprawdę kiepskim pomysłem do użycia w aplikacji komputerowej, ponieważ nie jest uwzględniony w profilu klienta; po co instalować 100 mln dodatkowych bibliotek na komputerze klienckim, aby użyć tylko jednej prostej metody? Wygląda jednak na to, że Microsoft nie ma lepszego pomysłu. Jedynym sposobem jest wdrożenie własnej metody lub użycie implementacji typu open source.
Vitalii
2
VASoftOnline: W takim przypadku możesz użyć implementacji Mono ParseQueryString: github.com/mono/mono/blob/master/mcs/class/System.Web/... licencja na to MIT X11: github.com/mono/mono / blob / master / LICENCJA
sw.
3
HttpUtility.ParseQueryStringbyłoby moją rekomendacją, z wyjątkiem tego, że w przypadku HttpUtility.ParseQueryString("&X=1&X=2&X=3")wyniku jest to, że ....X=1,2,3...posiadanie wielu parametrów o tej samej nazwie jest rzadkie, ale potrzebne do obsługi parametrów kontrolera, takich jak int [], IEnumerable <int> itp. (takich parametrów można użyć do obsługi wielu pól wyboru) patrz „Wiele wystąpień tej samej zmiennej ciągu zapytania jest skonsolidowanych w jednym wpisie” zgodnie z witryną MS . Ręcznie przygotowana wersja metody może być twoją jedyną opcją
dunxz
43

HttpUtility.ParseQueryString będzie działać tak długo, jak jesteś w aplikacji internetowej, lub nie przeszkadza ci to zależność od System.Web. Innym sposobem na to jest:

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
źródło
5
należy sprawdzić, czy części. Długość> 1, aby mieć pewność, że można wywołać części [1]
Alexandru Pupsa
2
Co jeśli nie ma wartości? np. może wyglądać ciąg zapytania ?foo=1&bar. HttpUtilityparsowałby to jako{ key = null, value = "bar" }
Thomas Levesque,
Jest to świetne w porównaniu do HttpUtility.ParseQueryString, ponieważ nie dekoduje wartości (więc wartości zakodowane w base64 są zachowane)
CRice
1
W rzeczywistości nadal musisz zezwolić na wartość „=” na przykład & code = A0SYw34Hj / m ++ lH0s7r0l / yg6GWdymzSCbI2zOn3V4o = spowoduje usunięcie ostatniego znaku „=”. Przepisałem część kodu, aby uzyskać pierwszy indeks „=” i podciągnąć klucz i wartość, aby to naprawić (zamiast używania podziału).
CRice
28

Wiele odpowiedzi zawiera niestandardowe przykłady z powodu zależności zaakceptowanej odpowiedzi od System.Web . Z pakietu Microsoft.AspNet.WebApi.Client NuGet dostępna jest metoda UriExtensions.ParseQueryString , której można również użyć:

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

Więc jeśli chcesz uniknąć zależności System.Web i nie chcesz tworzyć własnych, jest to dobra opcja.

James Skimming
źródło
3
Ta sama funkcja istnieje jako rozszerzenie w przestrzeni nazw System.Net.Http (patrz moja odpowiedź poniżej), nie ma potrzeby kolejnej całej zależności ...
jvenema 24.10.2015
@jvenema Skąd dodajesz zależność System.Net.Http.Formatting, uważam, że jest ona zapewniona tylko przez dodanie pakietu Microsoft.AspNet.WebApi.Client NuGet.
James Skimming
19

Chciałem usunąć zależność od System.Web, aby móc przeanalizować ciąg zapytania wdrożenia ClickOnce, mając jednocześnie wymagania wstępne ograniczone do „podzbioru środowiska tylko dla klienta”.

Podobała mi się odpowiedź RP. Dodałem dodatkową logikę.

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
źródło
jest to naprawdę pomocne dla Windows Phone, wystarczy zastąpić „NameValueCollection” na „SortedDictionnary <string, string>”
Mike Bryant
4
Zachowaj ostrożność podczas przełączania NameValueCollection dla Dictionary - to nie to samo! Ciągi zapytań obsługują wiele kluczy o tej samej wartości, podobnie jak NameValueCollection.
Matt DeKrey
7

Potrzebowałem funkcji, która jest nieco bardziej uniwersalna niż ta, która była już dostępna podczas pracy z zapytaniami OLSC.

  • Wartości mogą zawierać wiele znaków równości
  • Dekoduj zakodowane znaki zarówno w nazwie, jak i wartości
  • Może działać w Client Framework
  • Może działać na Mobile Framework.

Oto moje rozwiązanie:

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

Być może nie jest to zły pomysł <Extension()>, aby dodać to samo, aby dodać zdolność do samego Uri.

Josh Brown
źródło
7

Aby to zrobić bez System.Web, bez samodzielnego pisania i bez dodatkowych pakietów NuGet:

  1. Dodaj odniesienie do System.Net.Http.Formatting
  2. Dodaj using System.Net.Http;
  3. Użyj tego kodu:

    new Uri(uri).ParseQueryString()

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

jvenema
źródło
3
Skąd dodajesz zależność System.Net.Http.Formatting, uważam, że jest ona udostępniana tylko poprzez dodanie pakietu Microsoft.AspNet.WebApi.Client NuGet.
James Skimming
Huh, mój zły. Wydaje mi się, że przyszedł ze automatycznie instalowanym frameworkiem MVC, więc nie musiałem dodawać żadnych pakietów add'l (Program Files (x86) \ Microsoft ASP.NET \ ASP.NET MVC 4 \ Assemblies \ System.Net. Http.Formatting.dll)
jvenema
System.Net.Formatting EXTENSION VS2019 to osobna lista lub karta z tradycyjnych odniesień do ram
Sql Surfer
3

Jeśli chcesz uniknąć zależności od System.Web, która jest wymagana do użycia HttpUtility.ParseQueryString , możesz użyć Urimetody rozszerzenia ParseQueryStringznalezionej w System.Net.Http.

Pamiętaj, aby dodać odniesienie (jeśli jeszcze tego nie zrobiłeś) do System.Net.Httpswojego projektu.

Pamiętaj, że musisz przekonwertować treść odpowiedzi na poprawną Uri, aby ParseQueryString(in System.Net.Http) działała.

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
źródło
System.Net.Formatting EXTENSION VS2019 oddziela odniesienia do rozszerzeń od tradycyjnych odniesień do ram.
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.
źródło
5
średnik jest również dozwolony jako separator parametrów w http, lepiej nie wymyślać koła ponownie
Matthew Lock
2

Właśnie zdałem sobie sprawę, że klient Web API ma ParseQueryStringmetodę rozszerzenia, która działa na a Urii zwraca HttpValueCollection:

var parameters = uri.ParseQueryString();
string foo = parameters["foo"];
Thomas Levesque
źródło
1

Wystarczy uzyskać dostęp do Request.QueryString. AllKeys wymienione jako kolejna odpowiedź po prostu daje ci zestaw kluczy.

Ogar
źródło
1

HttpUtility.ParseQueryString(Request.Url.Query)return is HttpValueCollection(klasa wewnętrzna). Dziedziczy po 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
źródło
1

Jeśli nie chcesz zależności System.Web, po prostu wklej ten kod źródłowy z klasy HttpUtility.

Właśnie wymieszałem to z kodu źródłowego Mono . Zawiera HttpUtility i wszystkie jego zależności (takie jak IHtmlString, Helpers, HttpEncoder, HttpQSCollection).

Następnie użyj HttpUtility.ParseQueryString.

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


źródło
1

Ponieważ wszyscy wydają się wklejać swoje rozwiązanie .. oto moje :-) Potrzebowałem tego z biblioteki klasowej bez System.Webpobierania parametrów id z przechowywanych hiperłączy.

Myślałem, że podzielę się, ponieważ uważam to rozwiązanie za szybsze i lepiej wyglądające.

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

Stosowanie:

Statics.QueryGet(url, "id")
Tiele Declercq
źródło
1
Tylko jeden problem z tą metodą: ciąg zapytania może mieć więcej niż jedną wartość dla danego parametru. W takim przypadku metoda zgłosi błąd klucza duplikatu.
Thomas Levesque,
tak, przynajmniej użyj NameValueCollection, kwerendy ze zduplikowanymi kluczami są całkowicie legalne.
Chad Grant,
Ponadto w słowniku rozróżniana jest wielkość liter, więc X = 1 i x = 1 będą różnymi kluczami. Potrzebujesz nowego słownika <ciąg, ciąg> (StringComparer.OrdinalIgnoreCase);
Chad Grant
0

Hit Request.QueryString.Keys dla NameValueCollection wszystkich parametrów ciągu zapytania.

Mark Glorie
źródło
0

Aby uzyskać wszystkie wartości Querystring, spróbuj:

    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
źródło
0
        var q = Request.QueryString;
        NameValueCollection qscoll = HttpUtility.ParseQueryString(q.ToString());
Hamit YILDIRIM
źródło
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
źródło
-1

Tłumaczę na josh-brown w wersji C # w 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
źródło
-2

To jest mój kod, myślę, że jest bardzo przydatny:

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
źródło