NameValueCollection zur URL-Abfrage?


72

Ich weiß ich kann das

var nv = HttpUtility.ParseQueryString(req.RawUrl);

Aber gibt es eine Möglichkeit, dies wieder in eine URL umzuwandeln?

var newUrl = HttpUtility.Something("/page", nv);

Antworten:


110

Durch einfaches Aufrufen ToString()von NameValueCollectionwerden die Name-Wert-Paare in einem name1=value1&name2=value2Querystring-fähigen Format zurückgegeben. Beachten Sie, dass NameValueCollectionTypen dies nicht unterstützen und es irreführend ist, dies vorzuschlagen, aber das Verhalten funktioniert hier aufgrund des internen Typs, der tatsächlich zurückgegeben wird, wie unten erläutert.

Vielen Dank an @mjwills für den Hinweis, dass die HttpUtility.ParseQueryStringMethode tatsächlich ein internes HttpValueCollectionObjekt anstelle eines regulären Objekts zurückgibt NameValueCollection( trotz der angegebenen DokumentationNameValueCollection ). Die HttpValueCollectionkodiert automatisch die Abfragezeichenfolgeflag bei der Verwendung ToString(), so gibt es keine Notwendigkeit , eine Routine zu schreiben , dass Schleifen durch die Sammlung und die verwendete UrlEncodeMethode. Das gewünschte Ergebnis wird bereits zurückgegeben.

Mit dem vorliegenden Ergebnis können Sie es dann an die URL anhängen und umleiten:

var nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
string url = Request.Url.AbsolutePath + "?" + nameValues.ToString();
Response.Redirect(url);

Derzeit ist die einzige Möglichkeit, a zu verwenden HttpValueCollection, die ParseQueryStringoben gezeigte Methode (außer natürlich Reflexion). Es sieht so aus, als würde sich dies nicht ändern, da das Connect-Problem, bei dem diese Klasse veröffentlicht werden soll, mit dem Status "Wird nicht behoben" geschlossen wurde.

Nebenbei können Sie die Methoden ,, und aufrufen Add, um die Querystring-Elemente vor dem Anhängen zu ändern. Wenn Sie daran interessiert sind, sehen Sie meine Antwort auf eine andere Frage .SetRemovenameValues


3
Ich bin mir nicht sicher, ob nameValues.ToString () ordnungsgemäß maskiert wird.

9
Eigentlich können Sie einfach qs.ToString () verwenden. Dies liegt daran, dass qs keine NameValueCollection ist (trotz der Methodensignatur von HttpUtility.ParseQueryString). Es ist eigentlich ein privater Typ namens System.Web.HttpValueCollection (Sie können dies mit qs.GetType () überprüfen). NameValueCollection.ToString () codiert nicht per URL, HttpValueCollection.ToString () jedoch nicht. Ihre Verwendung von StringBuilder ist also völlig unnötig.
mjwills

8
Mein früherer Kommentar ist wahrscheinlich falsch. HttpValueCollection ruft nicht HttpUtility.UrlEncode auf, sondern HttpUtility.UrlEncodeUnicode ( tinyurl.com/HttpValue ). Dies ist ein Problem, da sich die beiden darin unterscheiden, wie sie mit einigen Zeichen umgehen (z. B. é). Die neue Dokumentation für UrlEncodeUnicode scheint darauf hinzudeuten, dass es nicht verwendet werden sollte - msdn.microsoft.com/en-us/library/… .
mjwills

42
NameValueCollection.ToString()kehrt System.Collections.Specialized.NameValueCollectionstring (wörtlich), nicht die „name Wert - Paare in einer Abfragezeichen bereit Format“.
Igor Brejc

5
@IgorBrejc danke, ich habe meinen Beitrag aktualisiert, um zu sagen, dass es irreführend ist, anzugeben, dass NameValueCollectiondas erwartete Format zurückgegeben wird. Die MSDN-Dokumentation ist auch irreführend, da sie besagt, dass ParseQueryStringa zurückgegeben wird, NameValueCollectionobwohl dies tatsächlich der Fall ist, HttpValueCollectionund wenn Sie dies aufrufen ToString(), sollte das erwartete Format zurückgegeben werden, wie in den restlichen Antworten erwähnt.
Ahmad Mageed

66
string q = String.Join("&",
             nvc.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(nvc[a])));

Diese Antwort ist die einzige, die die Codierung in den URL-Parametern korrekt verarbeitet. Ein großes Lob!
ConfusedAboutCPP

7
auch die einzige, die die wörtliche Frage bezüglich NameValueCollectionsund nicht das bereitgestellte Szenario beantwortet, das HttpValueCollectionstattdessen beinhaltet
drzaus

4
Ich denke nicht, dass dies richtig funktioniert, wenn es mehrere Werte für denselben Schlüssel gibt.
Martin Brown

3
@ MartinBrown ist richtig, tut es nicht. Folgendes funktioniert mit mehrwertigen Namenswertkollektionen: uriBuilder.Query + = String.Join ("&", parameters.AllKeys.SelectMany (key => (parameters.GetValues ​​(key) ?? Enumerable.Empty <string> ()). Select ( val => String.Concat (Schlüssel, "=", WebUtility.UrlEncode (val))));
Alex Marshall

3
Sie sollten den Schlüssel auch codieren: string q = String.Join("&", nvc.AllKeys.Select(a => HttpUtility.UrlEncode(a) + "=" + HttpUtility.UrlEncode(nvc[a])));
SoftDev

20

Erstellen Sie eine Erweiterungsmethode, die einige Schleifen verwendet. Ich bevorzuge diese Lösung, weil sie lesbar ist (kein Linq), keine System.Web.HttpUtility erfordert und doppelte Schlüssel verarbeitet.

public static string ToQueryString(this NameValueCollection nvc)
{
    if (nvc == null) return string.Empty;

    StringBuilder sb = new StringBuilder();

    foreach (string key in nvc.Keys)
    {
        if (string.IsNullOrWhiteSpace(key)) continue;

        string[] values = nvc.GetValues(key);
        if (values == null) continue;

        foreach (string value in values)
        {
            sb.Append(sb.Length == 0 ? "?" : "&");
            sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
        }
    }

    return sb.ToString();
}

Beispiel

var queryParams = new NameValueCollection()
{
    { "order_id", "0000" },
    { "item_id", "1111" },
    { "item_id", "2222" },
    { null, "skip entry with null key" },
    { "needs escaping", "special chars ? = &" },
    { "skip entry with null value", null }
};

Console.WriteLine(queryParams.ToQueryString());

Ausgabe

?order_id=0000&item_id=1111&item_id=2222&needs%20escaping=special%20chars%20%3F%20%3D%20%26

16

Dies sollte ohne zu viel Code funktionieren:

NameValueCollection nameValues = HttpUtility.ParseQueryString(String.Empty);
nameValues.Add(Request.QueryString);
// modify nameValues if desired
var newUrl = "/page?" + nameValues;

Die Idee ist, HttpUtility.ParseQueryStringeine leere Typensammlung zu generieren HttpValueCollection. Diese Klasse ist eine Unterklasse NameValueCollection, die als gekennzeichnet ist, internalsodass Ihr Code keine Instanz davon erstellen kann.

Das Schöne daran HttpValueCollectionist, dass die ToStringMethode die Codierung für Sie übernimmt. Durch die Nutzung der NameValueCollection.Add(NameValueCollection)Methode können Sie die vorhandenen Parameter für Abfragezeichenfolgen zu Ihrem neu erstellten Objekt hinzufügen, ohne die Request.QueryStringSammlung zuerst in eine URL-codierte Zeichenfolge konvertieren und dann wieder in eine Sammlung analysieren zu müssen.

Diese Technik kann auch als Erweiterungsmethode verwendet werden:

public static string ToQueryString(this NameValueCollection nameValueCollection)
{
    NameValueCollection httpValueCollection = HttpUtility.ParseQueryString(String.Empty);
    httpValueCollection.Add(nameValueCollection);
    return httpValueCollection.ToString();
}

7

Eigentlich sollten Sie auch den Schlüssel codieren, nicht nur den Wert.

string q = String.Join("&",
nvc.AllKeys.Select(a => $"{HttpUtility.UrlEncode(a)}={HttpUtility.UrlEncode(nvc[a])}"));

3

Die kurze Antwort ist, .ToString()auf dem zu verwenden NameValueCollectionund es mit der ursprünglichen URL zu kombinieren.

Ich möchte jedoch auf einige Dinge hinweisen:

Sie können nicht HttpUtility.ParseQueryStringauf verwenden Request.RawUrl. Die ParseQueryString()Methode sucht nach einem Wert wie diesem : ?var=value&var2=value2.

Wenn Sie einen NameValueCollectionder QueryStringParameter erhalten möchten, verwenden Sie einfach Request.QueryString().

var nv = Request.QueryString;

Um die URL neu zu erstellen, verwenden Sie einfach nv.ToString ().

string url = String.Format("{0}?{1}", Request.Path, nv.ToString());

Wenn Sie versuchen, eine URL-Zeichenfolge zu analysieren, anstatt das RequestObjekt zu verwenden, verwenden Sie Uriund die HttpUtility.ParseQueryStringMethode.

Uri uri = new Uri("<THE URL>");
var nv = HttpUtility.ParseQueryString(uri.Query);
string url = String.Format("{0}?{1}", uri.AbsolutePath, nv.ToString());

5
Wie ein Kommentar zu der akzeptierten Antwort, auf den hingewiesen wird (und der sich hier im Grunde wiederholt), Request.QueryStringist es eigentlich keine NameValueCollection, sondern eine spezielle Ableitung HttpValueCollection, weshalb es .ToStringfunktioniert. auf einem normalen nvc müssen Sie stattdessen so etwas wie diese Antwort verwenden
drzaus

3

Da a NameValueCollectionmehrere Werte für denselben Schlüssel haben kann, können Sie Folgendes berücksichtigen, wenn Sie sich mit dem Format des Abfragerings befassen (da dieser als durch Kommas getrennte Werte und nicht als "Array-Notation" zurückgegeben wird).

Beispiel

var nvc = new NameValueCollection();
nvc.Add("key1", "val1");
nvc.Add("key2", "val2");
nvc.Add("empty", null);
nvc.Add("key2", "val2b");

Verwandeln Sie sich in: key1=val1&key2[]=val2&empty&key2[]=val2beher als key1=val1&key2=val2,val2b&empty.

Code

string qs = string.Join("&", 
    // "loop" the keys
    nvc.AllKeys.SelectMany(k => {
        // "loop" the values
        var values = nvc.GetValues(k);
        if(values == null) return new[]{ k };
        return nvc.GetValues(k).Select( (v,i) => 
            // 'gracefully' handle formatting
            // when there's 1 or more values
            string.Format(
                values.Length > 1
                    // pick your array format: k[i]=v or k[]=v, etc
                    ? "{0}[]={1}"
                    : "{0}={1}"
                , k, HttpUtility.UrlEncode(v), i)
        );
    })
);

oder wenn du Linq nicht so sehr magst ...

string qs = nvc.ToQueryString(); // using...

public static class UrlExtensions {
    public static string ToQueryString(this NameValueCollection nvc) {
        return string.Join("&", nvc.GetUrlList());
    }

    public static IEnumerable<string> GetUrlList(this NameValueCollection nvc) {
        foreach(var k in nvc.AllKeys) {
            var values = nvc.GetValues(k);
            if(values == null)  { yield return k; continue; }
            for(int i = 0; i < values.Length; i++) {
                yield return
                // 'gracefully' handle formatting
                // when there's 1 or more values
                string.Format(
                    values.Length > 1
                        // pick your array format: k[i]=v or k[]=v, etc
                        ? "{0}[]={1}"
                        : "{0}={1}"
                    , k, HttpUtility.UrlEncode(values[i]), i);
            }
        }
    }
}

Wie bereits in den Kommentaren erwähnt, befassen sich die meisten anderen Antworten mit Ausnahme dieser Antwort eher mit dem Szenario ( Request.QueryStringist ein HttpValueCollection"nicht" a NameValueCollection) als mit der wörtlichen Frage.

Update: Nullwertproblem aus Kommentar behoben.


Gute Idee, aber Ihr Linq-Code sucht nicht nach Null. Wie können Sie sicherstellen, dass nvc.GetValues ​​(k) nicht null zurückgibt?
CodingTT

@ tianxu0836 guter Anruf; Ich nahm an, dass ein Schlüssel ohne Wert eine leere Zeichenfolge zurückgeben würde, aber rate nicht.
Drzaus


1

Ich verwende UriBuilder immer, um eine URL mit einem Querystring zurück in eine gültige und ordnungsgemäß codierte URL zu konvertieren.

var url = "http://my-link.com?foo=bar";

var uriBuilder = new UriBuilder(url);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query.Add("yep", "foo&bar");

uriBuilder.Query = query.ToString();
var result = uriBuilder.ToString();

// http://my-link.com:80/?foo=bar&yep=foo%26bar

0

Wie von @Atchitutchuk vorgeschlagen, können Sie QueryHelpers.AddQueryString in ASP.NET Core verwenden:

    public string FormatParameters(NameValueCollection parameters)
    {
        var queryString = "";
        foreach (var key in parameters.AllKeys)
        {
            foreach (var value in parameters.GetValues(key))
            {
                queryString = QueryHelpers.AddQueryString(queryString, key, value);
            }
        };

        return queryString.TrimStart('?');
    }

0

Das hat den Trick für mich getan:

public ActionResult SetLanguage(string language = "fr_FR")
        {            
            Request.UrlReferrer.TryReadQueryAs(out RouteValueDictionary parameters);            
            parameters["language"] = language;            
            return RedirectToAction("Index", parameters);            
        }

-1

Sie können verwenden.

var ur = new Uri("/page",UriKind.Relative);

Wenn dieses nv vom Typ string ist, können Sie es an den ersten Parameter uri anhängen. Mögen

var ur2 = new Uri("/page?"+nv.ToString(),UriKind.Relative);

+ nv ... das wird nicht richtig entkommen.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.