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);
Ahmad Mageed
quelle

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

Ahmad Mageed
quelle
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])));
Denis
quelle
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
Mathew Leger
quelle
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();
}
Dana
quelle
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])}"));
user2397863
quelle
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());
Jeremy
quelle
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.

drzaus
quelle
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
2

In AspNet Core 2.0 können Sie die QueryHelpers AddQueryString-Methode verwenden.

Atchitutchuk
quelle
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
ms007
quelle
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('?');
    }
arni
quelle
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);            
        }
stan
quelle
-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);
Gabriel Guimarães
quelle
+ nv ... das wird nicht richtig entkommen.