Construisez chaîne de requête pour le système.Net.HttpClient get

si je souhaite soumettre une requête http get en utilisant le système.Net.HttpClient il ne semble pas y avoir d'api pour ajouter des paramètres, est-ce correct?

y a-t-il une api simple disponible pour construire la chaîne de requête qui n'implique pas la construction d'une collection de valeurs de nom et d'url encodant ceux-ci et finalement les concaténer? J'espérais utiliser quelque chose comme L'api RestSharp (I. e AddParameter(..))

122
demandé sur Mike Perrenoud 2013-06-14 00:16:21

15 réponses

si je souhaite soumettre une requête http get en utilisant le système.Net.HttpClient il semble y avoir aucune api pour ajouter des paramètres, est-ce correct?

Oui.

y a-t-il une api simple disponible pour construire la chaîne de requête qui n'implique pas la création d'une collection de valeurs de nom et d'encodage d'url ceux-là et les concaténer finalement?

bien Sûr:

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

vous donnera le résultat attendu:

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

vous pourriez également trouver la classe UriBuilder utile:

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();

vous donnera le résultat attendu:

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

que vous pourriez plus que nourrir en toute sécurité à votre méthode HttpClient.GetAsync .

214
répondu Darin Dimitrov 2015-10-07 16:02:20

pour ceux qui ne veulent pas inclure System.Web dans les projets qui ne l'utilisent pas déjà, vous pouvez utiliser System.Net.Http et faire quelque chose comme ce qui suit:

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

version du dictionnaire

var query = new FormUrlEncodedContent(new Dictionary<string, string>()
{
    { "ham", "Glaced?"},
    { "x-men", "Wolverine + Logan"},
    { "Time", DateTime.UtcNow.ToString() },
}).ReadAsStringAsync().Result;
57
répondu Rostov 2018-08-02 23:59:05

TL; DR: NE PAS utiliser la version acceptée car il est complètement cassé en relation avec la manipulation des caractères unicode, et ne jamais utiliser L'API interne

j'ai trouvé un problème de double encodage bizarre avec la solution acceptée:

donc, si vous avez affaire à des caractères qui doivent être encodés, la solution acceptée conduit à un double encodage:

  • query les paramètres sont encodés automatiquement en utilisant NameValueCollection indexeur ( et cela utilise UrlEncodeUnicode , pas prévu régulier UrlEncode (!) )
  • puis, quand vous appelez uriBuilder.Uri il crée de nouveaux Uri en utilisant le constructeur qui ne encodant une fois de plus (encodage url normal)
  • qui ne peut pas être évitée en faisant uriBuilder.ToString() (même si ce retour est correct Uri qui IMO est au moins incohérence, peut-être un bug, mais c'est une autre question) et ensuite en utilisant HttpClient méthode accepter chaîne de caractères - client crée encore Uri de votre chaîne de caractères passé comme ceci: new Uri(uri, UriKind.RelativeOrAbsolute)

Petite, mais pleine repro:

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!

sortie:

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

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

Comme vous pouvez le voir, peu importe si vous faites uribuilder.ToString() + httpClient.GetStringAsync(string) ou uriBuilder.Uri + httpClient.GetStringAsync(Uri) vous finissez par envoyer double paramètre encodé

exemple fixe pourrait être:

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

mais cela utilise obsolète "1519240920 Uri constructeur

P. S sur mon dernier .NET sur Windows Server, Uri constructeur avec bool doc commentaire dit "obsolète, dontEscape est toujours faux", mais en fait, il fonctionne comme prévu (ignore s'échapper)

donc il ressemble à un autre bogue...

et même cela est tout à fait faux - il envoie UrlEncodedUnicode au serveur, pas seulement UrlEncoded ce que le serveur attend

mise à jour: une autre chose est, NameValueCollection fait effectivement UrlEncodeUnicode, qui n'est pas censé être utilisé plus et est incompatible avec l'url régulière.encoder / décoder (voir NameValueCollection à la requête D'URL? ).

donc le résultat est: n'utilisez jamais ce hack avec NameValueCollection query = HttpUtility.ParseQueryString(builder.Query); car il va Messer vos paramètres de requête unicode. Il suffit de compiler la requête manuellement et de l'assigner à UriBuilder.Query qui fera l'encodage nécessaire et ensuite obtenir Uri en utilisant UriBuilder.Uri .

premier exemple de se blesser en utilisant un code qui n'est pas censé être utilisé comme ceci

27
répondu illegal-immigrant 2017-05-23 12:34:39

vous pourriez vouloir vérifier Flurl [divulgation: je suis l'auteur], un constructeur d'URL fluent avec lib compagnon optionnel qui l'étend dans un client repos complet.

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 les docs pour plus de détails. Le paquet complet est disponible sur NuGet:

PM> Install-Package Flurl.Http

ou tout simplement le stand-alone de création d'URL:

PM> Install-Package Flurl

18
répondu Todd Menier 2018-06-19 14:17:56

dans un ASP.NET vous pouvez utiliser la classe QueryHelpers.

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

var response = await client.GetAsync(QueryHelpers.AddQueryString("/api/", query));
11
répondu Magu 2017-07-10 07:51:04

Darin a offert une solution intéressante et intelligente, et voici quelque chose qui pourrait être une autre option:

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();
    }
}

et donc en l'utilisant, vous pourriez faire ceci:

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

var url = ...
url += "?" + parms;
3
répondu Mike Perrenoud 2013-06-13 20:29:33

Ou tout simplement à l'aide de mon extension de l'Uri

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());
}

Utilisation

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

résultat

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

2
répondu Roman Ratskey 2013-07-30 01:30:58

Le RFC 6570 URI Modèle de bibliothèque je suis en développement est capable d'effectuer cette opération. Tout encodage est géré pour vous conformément à ce RFC. Au moment d'écrire ces lignes, une version bêta est disponible et la seule raison pour laquelle elle n'est pas considérée comme une version stable 1.0 est que la documentation ne répond pas entièrement à mes attentes (voir #17 , #18 , #32 , #43 ).

vous pouvez soit construire une chaîne de requête seul:

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

ou vous pouvez construire un URI complet:

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
répondu Sam Harwell 2014-08-01 15:38:45

puisque je dois réutiliser ce peu de temps, je suis venu avec cette classe qui aide simplement à abstraire comment la chaîne de requête est composée.

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

}

l'utilisation sera simplifiée à quelque chose comme ceci:

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

qui rendra l'uri: http://example.com/?foo=bar%3c%3e%26-baz&bar=second

1
répondu Jaider 2015-02-18 21:30:17

vous pouvez toujours utiliser IEnterprise.Facile-HTTP parce qu'il a un construit dans le constructeur de requête:

await new RequestBuilder<ExampleObject>()
.SetHost("https://httpbin.org")
.SetContentType(ContentType.Application_Json)
.SetType(RequestType.Get)
.ContinueToQuery()
    .SetQuery("/get")
    .ParseModelToQuery(dto)
    .Build()
.Build()
.Execute();
1
répondu Nikolay Hristov 2018-03-28 12:40:27

grâce à "Darin Dimitrov", ce sont les méthodes d'extension.

 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
répondu Waleed A.K. 2015-03-03 21:37:03

c'est ma solution pour .net Core basée dans la réponse de la Ratskey Romaine. Le type NameValueCollection a été retiré dans .Net Core.

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

Utilisation

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

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

résultat

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

0
répondu Angelo Polotto 2017-04-17 13:38:57

pour éviter les problèmes de double encodage décrits dans taras.réponse de roshko et de garder la possibilité de travailler facilement avec les paramètres de requête, vous pouvez utiliser uriBuilder.Uri.ParseQueryString() au lieu de HttpUtility.ParseQueryString() .

0
répondu Valeriy Lyuchyn 2017-04-19 15:39:28

bonne partie de la réponse acceptée, modifiée pour utiliser UriBuilder.URI.ParseQueryString () au lieu de 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();
0
répondu Jpsy 2018-09-19 09:29:47

Je ne pouvais pas trouver une meilleure solution que de créer une méthode d'extension pour convertir un dictionnaire en QueryStringFormat. La solution proposée par Waleed A. K. est également bonne.

suis ma solution:

créer la méthode d'extension:

public static class DictionaryExt
{
    public static string ToQueryString<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
    {
        return ToQueryString<TKey, TValue>(dictionary, "?");
    }

    public static string ToQueryString<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, string startupDelimiter)
    {
        string result = string.Empty;
        foreach (var item in dictionary)
        {
            if (string.IsNullOrEmpty(result))
                result += startupDelimiter; // "?";
            else
                result += "&";

            result += string.Format("{0}={1}", item.Key, item.Value);
        }
        return result;
    }
}

et eux:

var param = new Dictionary<string, string>
          {
            { "param1", "value1" },
            { "param2", "value2" },
          };
param.ToQueryString(); //By default will add (?) question mark at begining
//"?param1=value1&param2=value2"
param.ToQueryString("&"); //Will add (&)
//"&param1=value1&param2=value2"
param.ToQueryString(""); //Won't add anything
//"param1=value1&param2=value2"
-1
répondu Diego Mendes 2015-06-03 00:44:30