it-swarm.com.de

Erstellen Sie eine Abfragezeichenfolge für System.Net.HttpClient get

Wenn ich eine HTTP-Get-Anfrage mit System.Net.HttpClient absenden möchte, scheint es keine API zu geben, um Parameter hinzuzufügen. Ist dies korrekt? 

Gibt es eine einfache API, um die Abfragezeichenfolge zu erstellen, bei der keine Namenswertauflistung erstellt und die URL codiert und anschließend verkettet werden muss? )

132
NeoDarque

Wenn ich eine http-Anfrage mit System.Net.HttpClient .__ absenden möchte. Es scheint keine API zu geben, um Parameter hinzuzufügen. Ist das richtig?

Ja.

Gibt es eine einfache API, die zum Erstellen der Abfragezeichenfolge mit der Zeichenfolge .__ verfügbar ist. beinhaltet nicht das Erstellen einer Namenswertsammlung und der URL-Kodierung die und dann schließlich verketten?

Sicher:

var query = HttpUtility.ParseQueryString(string.Empty);
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
string queryString = query.ToString();

gibt Ihnen das erwartete Ergebnis:

foo=bar%3c%3e%26-baz&bar=bazinga

Vielleicht finden Sie auch die UriBuilder-Klasse nützlich:

var builder = new UriBuilder("http://example.com");
builder.Port = -1;
var query = HttpUtility.ParseQueryString(builder.Query);
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
builder.Query = query.ToString();
string url = builder.ToString();

gibt Ihnen das erwartete Ergebnis:

http://example.com/?foo=bar%3c%3e%26-baz&bar=bazinga

dass Sie Ihre HttpClient.GetAsync-Methode mehr als sicher verwenden können.

233
Darin Dimitrov

Für diejenigen, die System.Web nicht in Projekte einbeziehen möchten, die es nicht bereits verwenden, können Sie FormUrlEncodedContent aus System.Net.Http verwenden und etwa Folgendes tun:

keyvaluepair version

string query;
using(var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]{
    new KeyValuePair<string, string>("ham", "Glazed?"),
    new KeyValuePair<string, string>("x-men", "Wolverine + Logan"),
    new KeyValuePair<string, string>("Time", DateTime.UtcNow.ToString()),
})) {
    query = content.ReadAsStringAsync().Result;
}

wörterbuchversion

string query;
using(var content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
    { "ham", "Glaced?"},
    { "x-men", "Wolverine + Logan"},
    { "Time", DateTime.UtcNow.ToString() },
})) {
    query = content.ReadAsStringAsync().Result;
}
69
Rostov

TL; DR: Verwenden Sie keine akzeptierte Version, da sie in Bezug auf die Handhabung von Unicode-Zeichen vollständig beschädigt ist, und verwenden Sie niemals interne API.

Ich habe mit der akzeptierten Lösung ein seltsames Doppelkodierungsproblem gefunden:

Wenn Sie also mit Zeichen arbeiten, die codiert werden müssen, führt die akzeptierte Lösung zu einer doppelten Codierung:

  • abfrageparameter werden automatisch mit NameValueCollection indexer codiert (und dies verwendet UrlEncodeUnicode, nicht normal erwartet UrlEncode (!))
  • Wenn Sie dann uriBuilder.Uri aufrufen, erstellt er mit dem Konstruktor eine neue Uri, die noch einmal codiert (normale URL-Codierung).
  • Das kann nicht vermieden werden, indem uriBuilder.ToString() ausgeführt wird (obwohl dies korrekte Uri zurückgibt, deren IMO zumindest Inkonsistenz ist, möglicherweise ein Fehler, aber das ist eine andere Frage) und dann die HttpClient-Methode verwendet, die den String akzeptiert - client erstellt Uri noch aus Ihre übergebene Zeichenfolge wie folgt: new Uri(uri, UriKind.RelativeOrAbsolute)

Kleine, aber vollständige Reproduktion:

var builder = new UriBuilder
{
    Scheme = Uri.UriSchemeHttps,
    Port = -1,
    Host = "127.0.0.1",
    Path = "app"
};

NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);

query["cyrillic"] = "кирилиця";

builder.Query = query.ToString();
Console.WriteLine(builder.Query); //query with cyrillic stuff UrlEncodedUnicode, and that's not what you want

var uri = builder.Uri; // creates new Uri using constructor which does encode and messes cyrillic parameter even more
Console.WriteLine(uri);

// this is still wrong:
var stringUri = builder.ToString(); // returns more 'correct' (still `UrlEncodedUnicode`, but at least once, not twice)
new HttpClient().GetStringAsync(stringUri); // this creates Uri object out of 'stringUri' so we still end up sending double encoded cyrillic text to server. Ouch!

Ausgabe:

?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f

https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f

Wie Sie vielleicht sehen, egal, ob Sie uribuilder.ToString() + httpClient.GetStringAsync(string) oder uriBuilder.Uri + httpClient.GetStringAsync(Uri) ausführen, werden am Ende doppelt codierte Parameter gesendet

Festes Beispiel könnte sein:

var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);

Dies verwendet jedoch den obsoleteUri-Konstruktor

P.S auf meinem neuesten .NET unter Windows Server, Uri Konstruktor mit bool doc-Kommentar besagt "obsolet, dontEscape ist immer false", funktioniert aber tatsächlich wie erwartet (Überspringt das Escaping)

So sieht es aus wie ein weiterer Fehler ...

Und auch das ist einfach falsch - es sendet UrlEncodedUnicode an den Server, nicht nur UrlEncoded, was der Server erwartet

Update: Eine weitere Sache ist, dass NameValueCollection eigentlich UrlEncodeUnicode verwendet, der nicht mehr verwendet werden soll und nicht mit regulärem url.encode/decode kompatibel ist (siehe NameValueCollection zur URL-Abfrage? ).

Die letzte Zeile lautet: Verwenden Sie diesen Hack niemals mit NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);, da dies Ihre Unicode-Abfrageparameter durcheinander bringt. Erstellen Sie die Abfrage einfach manuell, und weisen Sie sie UriBuilder.Query zu, was die erforderliche Kodierung vornimmt, und rufen Sie dann Uri mithilfe von UriBuilder.Uri ab.

Erstes Beispiel dafür, wie Sie sich selbst verletzen, indem Sie Code verwenden, der nicht so verwendet werden soll

32

Vielleicht möchten Sie einen Blick auf Flurl [Disclosure: I'm the author] werfen, einen fließenden URL-Builder mit optionaler Companion-Bibliothek, der ihn zu einem vollständigen REST - Client erweitert.

var result = await "https://api.com"
    // basic URL building:
    .AppendPathSegment("endpoint")
    .SetQueryParams(new {
        api_key = ConfigurationManager.AppSettings["SomeApiKey"],
        max_results = 20,
        q = "Don't worry, I'll get encoded!"
    })
    .SetQueryParams(myDictionary)
    .SetQueryParam("q", "overwrite q!")

    // extensions provided by Flurl.Http:
    .WithOAuthBearerToken("token")
    .GetJsonAsync<TResult>();

Check out the docs für weitere Details. Das vollständige Paket ist auf NuGet verfügbar:

PM> Install-Package Flurl.Http

oder nur der eigenständige URL-Builder:

PM> Install-Package Flurl

20
Todd Menier

In einem ASP.NET Core-Projekt können Sie die QueryHelpers-Klasse verwenden. 

// using Microsoft.AspNetCore.WebUtilities;
var query = new Dictionary<string, string>
{
    ["foo"] = "bar",
    ["foo2"] = "bar2",
    // ...
};

var response = await client.GetAsync(QueryHelpers.AddQueryString("/api/", query));
16
Magu

Darin bot eine interessante und clevere Lösung, und hier ist etwas, was eine andere Option sein könnte:

public class ParameterCollection
{
    private Dictionary<string, string> _parms = new Dictionary<string, string>();

    public void Add(string key, string val)
    {
        if (_parms.ContainsKey(key))
        {
            throw new InvalidOperationException(string.Format("The key {0} already exists.", key));
        }
        _parms.Add(key, val);
    }

    public override string ToString()
    {
        var server = HttpContext.Current.Server;
        var sb = new StringBuilder();
        foreach (var kvp in _parms)
        {
            if (sb.Length > 0) { sb.Append("&"); }
            sb.AppendFormat("{0}={1}",
                server.UrlEncode(kvp.Key),
                server.UrlEncode(kvp.Value));
        }
        return sb.ToString();
    }
}

wenn Sie es verwenden, können Sie Folgendes tun:

var parms = new ParameterCollection();
parms.Add("key", "value");

var url = ...
url += "?" + parms;
3
Mike Perrenoud

Oder einfach meine Uri-Erweiterung verwenden

Code

public static Uri AttachParameters(this Uri uri, NameValueCollection parameters)
{
    var stringBuilder = new StringBuilder();
    string str = "?";
    for (int index = 0; index < parameters.Count; ++index)
    {
        stringBuilder.Append(str + parameters.AllKeys[index] + "=" + parameters[index]);
        str = "&";
    }
    return new Uri(uri + stringBuilder.ToString());
}

Verwendungszweck

Uri uri = new Uri("http://www.example.com/index.php").AttachParameters(new NameValueCollection
                                                                           {
                                                                               {"Bill", "Gates"},
                                                                               {"Steve", "Jobs"}
                                                                           });

Ergebnis

http://www.example.com/index.php?Bill=Gates&Steve=Jobs

2
Roman Ratskey

Die RFC 6570-URI-Vorlagenbibliothek die gerade entwickelt wird, kann diese Operation ausführen. Alle Kodierungen werden für Sie gemäß diesem RFC behandelt. Zum Zeitpunkt der Erstellung dieses Artikels ist eine Betaversion verfügbar. Der einzige Grund, warum es nicht als stabile Version 1.0 gilt, ist, dass die Dokumentation meine Erwartungen nicht vollständig erfüllt (siehe Ausgaben # 17 , # 18# 32 , # 43 ).

Sie können entweder eine Abfragezeichenfolge alleine erstellen:

UriTemplate template = new UriTemplate("{?params*}");
var parameters = new Dictionary<string, string>
  {
    { "param1", "value1" },
    { "param2", "value2" },
  };
Uri relativeUri = template.BindByName(parameters);

Oder Sie könnten eine vollständige URI erstellen:

UriTemplate template = new UriTemplate("path/to/item{?params*}");
var parameters = new Dictionary<string, string>
  {
    { "param1", "value1" },
    { "param2", "value2" },
  };
Uri baseAddress = new Uri("http://www.example.com");
Uri relativeUri = template.BindByName(baseAddress, parameters);
2
Sam Harwell

Sie können immer IEnterprise.Easy-HTTP verwenden, da in Query Builder ein integrierter Abfrage-Assistent installiert ist:

await new RequestBuilder<ExampleObject>()
.SetHost("https://httpbin.org")
.SetContentType(ContentType.Application_Json)
.SetType(RequestType.Get)
.ContinueToQuery()
    .SetQuery("/get")
    .ParseModelToQuery(dto)
    .Build()
.Build()
.Execute();
1
Nikolay Hristov

Ein guter Teil der akzeptierten Antwort, geändert zur Verwendung von UriBuilder.Uri.ParseQueryString () anstelle von HttpUtility.ParseQueryString ():

var builder = new UriBuilder("http://example.com");
var query = builder.Uri.ParseQueryString();
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
builder.Query = query.ToString();
string url = builder.ToString();
1
Jpsy

Da ich dieses wenige Mal wiederverwenden muss, habe ich mir diese Klasse ausgedacht, die einfach dazu beiträgt, wie der Abfrage-String zusammengesetzt wird.

public class UriBuilderExt
{
    private NameValueCollection collection;
    private UriBuilder builder;

    public UriBuilderExt(string uri)
    {
        builder = new UriBuilder(uri);
        collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
    }

    public void AddParameter(string key, string value) {
        collection.Add(key, value);
    }

    public Uri Uri{
        get
        {
            builder.Query = collection.ToString();
            return builder.Uri;
        }
    }

}

Die Verwendung wird in etwa wie folgt vereinfacht:

var builder = new UriBuilderExt("http://example.com/");
builder.AddParameter("foo", "bar<>&-baz");
builder.AddParameter("bar", "second");
var uri = builder.Uri;

das gibt den URI zurück: http://example.com/?foo=bar%3c%3e%26-baz&bar=second

1
Jaider

Um das in der Antwort von taras.roshko beschriebene Problem der Doppelkodierung zu vermeiden und die Möglichkeit zu behalten, problemlos mit Abfrageparametern zu arbeiten, können Sie uriBuilder.Uri.ParseQueryString() statt HttpUtility.ParseQueryString() verwenden.

0
Valeriy Lyuchyn

Dank "Darin Dimitrov" ist dies die Erweiterungsmethode.

 public static partial class Ext
{
    public static Uri GetUriWithparameters(this Uri uri,Dictionary<string,string> queryParams = null,int port = -1)
    {
        var builder = new UriBuilder(uri);
        builder.Port = port;
        if(null != queryParams && 0 < queryParams.Count)
        {
            var query = HttpUtility.ParseQueryString(builder.Query);
            foreach(var item in queryParams)
            {
                query[item.Key] = item.Value;
            }
            builder.Query = query.ToString();
        }
        return builder.Uri;
    }

    public static string GetUriWithparameters(string uri,Dictionary<string,string> queryParams = null,int port = -1)
    {
        var builder = new UriBuilder(uri);
        builder.Port = port;
        if(null != queryParams && 0 < queryParams.Count)
        {
            var query = HttpUtility.ParseQueryString(builder.Query);
            foreach(var item in queryParams)
            {
                query[item.Key] = item.Value;
            }
            builder.Query = query.ToString();
        }
        return builder.Uri.ToString();
    }
}
0
Waleed A.K.

Dies ist meine Lösung für .Net Core basierend auf der Antwort von Roman Ratskey. Der Typ NameValueCollection wurde in .Net Core entfernt.

Code

public static class UriExtensions
    {
        public static string AttachParameters(this string uri, Dictionary<string, string> parameters)
        {
            var stringBuilder = new StringBuilder();
            string str = "?";

            foreach (KeyValuePair<string, string> parameter in parameters)
            {
                stringBuilder.Append(str + parameter.Key + "=" + parameter.Value);
                str = "&";
            }
            return uri + stringBuilder;
        }
    }

Verwendungszweck

 var parameters = new Dictionary<string, string>();
            parameters.Add("Bill", "Gates");
            parameters.Add("Steve", "Jobs");

string uri = "http://www.example.com/index.php".AttachParameters(parameters);

Ergebnis

http://www.example.com/index.php?Bill=Gates&Steve=Jobs

0
Ângelo Polotto

In Anlehnung an Rostovs Beitrag können Sie, wenn Sie in Ihrem Projekt keinen Verweis auf System.Web einfügen möchten, FormDataCollection aus System.Net.Http.Formatting verwenden und folgendermaßen vorgehen:

Verwenden Sie System.Net.Http.Formatting.FormDataCollection

var parameters = new Dictionary<string, string>()
{
    { "ham", "Glaced?" },
    { "x-men", "Wolverine + Logan" },
    { "Time", DateTime.UtcNow.ToString() },
}; 
var query = new FormDataCollection(parameters).ReadAsNameValueCollection().ToString();
0
cwills