608 Stimmen

Wie man eine Abfragezeichenfolge für eine URL in C# erstellen?

Eine häufige Aufgabe beim Aufrufen von Webressourcen aus einem Code ist die Erstellung eines Abfrage-Strings, der alle erforderlichen Parameter enthält. Das ist zwar keine Raketenwissenschaft, aber es gibt ein paar raffinierte Details, um die man sich kümmern muss, wie zum Beispiel das Anhängen eines & wenn nicht der erste Parameter, Kodierung der Parameter usw.

Der Code dafür ist sehr einfach, aber ein bisschen mühsam:

StringBuilder SB = new StringBuilder();
if (NeedsToAddParameter A) 
{ 
  SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA")); 
}

if (NeedsToAddParameter B) 
{
  if (SB.Length>0) SB.Append("&"); 
  SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); }
}

Dies ist eine so häufige Aufgabe, dass man erwarten würde, dass es eine Hilfsklasse gibt, die dies eleganter und lesbarer macht. Beim Durchsuchen von MSDN habe ich keine gefunden, was mich zu der folgenden Frage bringt:

Was ist die eleganteste und sauberste Methode, die Sie kennen, um dies zu tun?

6 Stimmen

Sie verpassen nichts. Die Erstellung von Querystrings ist eine große Lücke im Framework, die ich versucht habe, mit Flurl .

0 Stimmen

Sie haben mich gerade auf die Idee gebracht, dass ich einen bauen sollte. new UrlBuilder(existing).AddQuery("key", "value").ToString()

863voto

John Bledsoe Punkte 16242

Sie können eine neue beschreibbare Instanz von HttpValueCollection durch den Aufruf System.Web.HttpUtility.ParseQueryString(string.Empty) und verwenden Sie es dann wie jede NameValueCollection . Sobald Sie die gewünschten Werte hinzugefügt haben, können Sie die ToString auf die Sammlung, um eine Abfragezeichenfolge zu erhalten, wie folgt:

NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);

queryString.Add("key1", "value1");
queryString.Add("key2", "value2");

return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded

Le site HttpValueCollection ist intern und Sie können daher nicht direkt eine Instanz erstellen. Sobald Sie jedoch eine Instanz erhalten haben, können Sie sie wie jede andere NameValueCollection . Da das eigentliche Objekt, mit dem Sie arbeiten, ein HttpValueCollection Wenn Sie die Methode ToString aufrufen, wird die überschriebene Methode auf HttpValueCollection die die Sammlung als URL-kodierte Abfragezeichenfolge formatiert.

Nachdem ich SO und im Internet nach einer Antwort auf ein ähnliches Problem gesucht habe, ist dies die einfachste Lösung, die ich finden konnte.

.NET Core

Wenn Sie in .NET Core arbeiten, können Sie die Microsoft.AspNetCore.WebUtilities.QueryHelpers Klasse, die dies stark vereinfacht.

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers

Beispiel-Code:

const string url = "https://customer-information.azure-api.net/customers/search/taxnbr";
var param = new Dictionary<string, string>() { { "CIKey", "123456789" } };

var newUrl = new Uri(QueryHelpers.AddQueryString(url, param));

6 Stimmen

Sie könnten wahrscheinlich eine Erweiterungsmethode namens ToURLQueryString für die IDictionary-Schnittstelle erstellen: public static string ToURLQueryString(this IDictionary dict) { ... }

74 Stimmen

Diese Methode ist nicht standardkonform für Multibyte-Zeichen. Es kodiert sie als %uXXXX anstelle von %XX%XX. Daraus resultierende Abfragezeichenfolgen können von Webservern falsch interpretiert werden. Dies ist sogar in der internen Framework-Klasse HttpValueCollection dokumentiert, die von HttpUtility.ParseQueryString() zurückgegeben wird. Der Kommentar besagt, dass dieses Verhalten aus Gründen der Abwärtskompatibilität beibehalten wird.

29 Stimmen

Beachten Sie, dass es einen wichtigen Unterschied zwischen HttpUtilityPraseQueryString("") und new NameValueCollection() gibt - nur das HttpUtility-Ergebnis überschreibt ToString(), um einen richtigen Querystring zu erzeugen

328voto

annakata Punkte 72408

Wenn Sie unter die Haube schauen, ist die Eigenschaft QueryString eine NameValueCollection. Wenn ich ähnliche Dinge getan habe, war ich in der Regel daran interessiert, Serialisierung UND Deserialisierung, so dass mein Vorschlag ist, eine NameValueCollection zu bauen und dann zu übergeben:

using System.Linq;
using System.Web;
using System.Collections.Specialized;

private string ToQueryString(NameValueCollection nvc)
{
    var array = (
        from key in nvc.AllKeys
        from value in nvc.GetValues(key)
            select string.Format(
                "{0}={1}",
                HttpUtility.UrlEncode(key),
                HttpUtility.UrlEncode(value))
        ).ToArray();
    return "?" + string.Join("&", array);
}

Ich stelle mir vor, dass es eine super elegante Möglichkeit gibt, dies auch in LINQ zu tun...

24 Stimmen

Die HTTP-Spezifikation (RFC 2616) sagt nichts darüber aus, was Abfragezeichenfolgen enthalten können. Ebenso wenig in RFC 3986, das das allgemeine URI-Format definiert. Das üblicherweise verwendete Format für Schlüssel/Wertpaare wird als application/x-www-form-urlencoded und ist eigentlich durch HTML definiert, um Formulardaten als Teil eines Formulars zu übermitteln. GET Anfrage. HTML 5 verbietet nicht mehrere Werte pro Schlüssel in diesem Format, sondern verlangt sogar, dass der Browser mehrere Werte pro Schlüssel erzeugt, falls die Seite (fälschlicherweise) mehrere Felder mit demselben name Attribut. Siehe goo.gl/uk1Ag

16 Stimmen

@annakata: Ich weiß, dass mein Kommentar über ein Jahr alt ist (und die Antwort über zwei Jahre!), aber NameValueCollection unterstützt sehr wohl mehrere Werte pro Schlüssel, indem sie die Methode GetValues(key) verwendet.

4 Stimmen

@MauricioScheffer: Aber NameValueCollection lässt sich nicht "korrekt" in einen Querystring umwandeln. Wenn Sie zum Beispiel den QueryString-Parameter auf WebClient setzen, wo derselbe Schlüssel mehrmals vorkommt, wird daraus "path?key=value1,value2" statt "path?key=value1&key=value2", was ein gängiges (Standard?) Muster ist.

115voto

Vedran Punkte 9308

Inspiriert von Roy Tinkers Kommentar habe ich eine einfache Erweiterungsmethode für die Uri-Klasse verwendet, die meinen Code übersichtlich und sauber hält:

using System.Web;

public static class HttpExtensions
{
    public static Uri AddQuery(this Uri uri, string name, string value)
    {
        var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);

        httpValueCollection.Remove(name);
        httpValueCollection.Add(name, value);

        var ub = new UriBuilder(uri);
        ub.Query = httpValueCollection.ToString();

        return ub.Uri;
    }
}

Verwendung:

Uri url = new Uri("http://localhost/rest/something/browse").
          AddQuery("page", "0").
          AddQuery("pageSize", "200");

Bearbeiten - Normenkonforme Variante

Wie mehrere Personen anmerkten, httpValueCollection.ToString() kodiert Unicode-Zeichen in einem nicht normenkonform Weise. Dies ist eine Variante der gleichen Erweiterungsmethode, die solche Zeichen durch den Aufruf von HttpUtility.UrlEncode Methode anstelle der veralteten HttpUtility.UrlEncodeUnicode Methode.

using System.Web;

public static Uri AddQuery(this Uri uri, string name, string value)
{
    var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);

    httpValueCollection.Remove(name);
    httpValueCollection.Add(name, value);

    var ub = new UriBuilder(uri);

    // this code block is taken from httpValueCollection.ToString() method
    // and modified so it encodes strings with HttpUtility.UrlEncode
    if (httpValueCollection.Count == 0)
        ub.Query = String.Empty;
    else
    {
        var sb = new StringBuilder();

        for (int i = 0; i < httpValueCollection.Count; i++)
        {
            string text = httpValueCollection.GetKey(i);
            {
                text = HttpUtility.UrlEncode(text);

                string val = (text != null) ? (text + "=") : string.Empty;
                string[] vals = httpValueCollection.GetValues(i);

                if (sb.Length > 0)
                    sb.Append('&');

                if (vals == null || vals.Length == 0)
                    sb.Append(val);
                else
                {
                    if (vals.Length == 1)
                    {
                        sb.Append(val);
                        sb.Append(HttpUtility.UrlEncode(vals[0]));
                    }
                    else
                    {
                        for (int j = 0; j < vals.Length; j++)
                        {
                            if (j > 0)
                                sb.Append('&');

                            sb.Append(val);
                            sb.Append(HttpUtility.UrlEncode(vals[j]));
                        }
                    }
                }
            }
        }

        ub.Query = sb.ToString();
    }

    return ub.Uri;
}

4 Stimmen

Perfekt. Zu meiner internen Bibliothek hinzugefügt :)

1 Stimmen

Sie sollten auch den Wert URL-kodieren. queryString.Add(name, Uri.EscapeDataString(value));

0 Stimmen

Prüfen Sie dies Zeile . / ist nicht kodiert.

64voto

Denis Nutiu Punkte 700

Seltsam, dass niemand QueryBuilder von AspNet.Core erwähnt hat.

Es ist hilfreich, wenn Sie eine Abfrage mit doppeltem Schlüssel haben wie ?filter=a&filter=b

            var qb = new QueryBuilder();
            qb.Add("filter", new string[] {"A", "B"};

Dann fügen Sie einfach qb an den URI an, es wird automatisch in eine Zeichenkette umgewandelt.

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.extensions.querybuilder?view=aspnetcore-5.0

4 Stimmen

Es gibt hier so viele Antworten, dass es eine Weile gedauert hat, diejenige zu finden, die in der aktuellen Zeit der Entwicklung mit .NET Core richtig ist.

1 Stimmen

Ich kann in dotnet core 3.1 nicht darauf zugreifen. Der Namespace-Import wird nicht gefunden und der Versuch, das Nuget-Paket hinzuzufügen nuget.org/packages/Microsoft.AspNetCore.App.Ref/3.1.10 führt zu einem Wiederherstellungsfehler error: NU1213: The package Microsoft.AspNetCore.App.Ref 3.1.10 has a package type DotnetPlatform that is incompatible with this project. error: Package 'Microsoft.AspNetCore.App.Ref' is incompatible with 'all' frameworks in project

0 Stimmen

Ich glaube, das Paket ist nur in dotnet 5 verfügbar.

50voto

Todd Menier Punkte 34732

Flurl (Offenlegung: Ich bin der Autor) unterstützt die Erstellung von Abfragezeichenfolgen über anonyme Objekte (neben anderen Möglichkeiten):

var url = "http://www.some-api.com".SetQueryParams(new
{
    api_key = ConfigurationManager.AppSettings["SomeApiKey"],
    max_results = 20,
    q = "Don't worry, I'll get encoded!"
});

Die optionale Flurl.Http-Begleitlib ermöglicht es Ihnen, HTTP-Aufrufe direkt aus der gleichen fließenden Aufrufkette zu tätigen und sie zu einem vollwertigen REST-Client zu erweitern:

T result = await "https://api.mysite.com"
    .AppendPathSegment("person")
    .SetQueryParams(new { ap_key = "my-key" })
    .WithOAuthBearerToken("MyToken")
    .PostJsonAsync(new { first_name = firstName, last_name = lastName })
    .ReceiveJson<T>();

Das vollständige Paket ist auf NuGet verfügbar:

PM> Install-Package Flurl.Http

oder nur den eigenständigen URL-Builder:

PM> Install-Package Flurl

0 Stimmen

Soweit ich versucht habe, ist dies die Lösung, die Ihnen die Möglichkeit, Arrays in Ihrem QueryString hinzufügen können!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X