Construire une chaîne de requête pour obtenir System.Net.HttpClient

Si je souhaite soumettre une requête http à l’aide de System.Net.HttpClient, il ne semble pas y avoir d’API pour append des parameters, est-ce correct?

Existe-t-il une simple API disponible pour générer la chaîne de requête qui n’implique pas la création d’une collection de valeurs de nom et d’une URL codant celles-ci et finalement les concaténant? J’espérais utiliser quelque chose comme l’api de RestSharp (c’est-à-dire AddParameter (..))

Si je souhaite soumettre une requête http à l’aide de System.Net.HttpClient, il ne semble pas y avoir d’API pour append des parameters, est-ce correct?

Oui.

Existe-t-il une simple API disponible pour générer la chaîne de requête qui n’implique pas la création d’une collection de valeurs de nom et d’une URL codant celles-ci et finalement les concaténant?

Sûr:

var query = HttpUtility.ParseQuerySsortingng(ssortingng.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.ParseQuerySsortingng(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 pouvez plus que nourrir en toute sécurité votre méthode HttpClient.GetAsync .

Pour ceux qui ne veulent pas inclure System.Web dans des projets qui ne l’utilisent pas déjà, vous pouvez utiliser System.Net.Http et faire quelque chose comme ceci:

version keyvaluepair

 ssortingng query; using(var content = new FormUrlEncodedContent(new KeyValuePair[]{ new KeyValuePair("ham", "Glazed?"), new KeyValuePair("x-men", "Wolverine + Logan"), new KeyValuePair("Time", DateTime.UtcNow.ToSsortingng()), })) { query = content.ReadAsSsortingngAsync().Result; } 

version du dictionnaire

 var query = new FormUrlEncodedContent(new Dictionary() { { "ham", "Glaced?"}, { "x-men", "Wolverine + Logan"}, { "Time", DateTime.UtcNow.ToSsortingng() }, }).ReadAsSsortingngAsync().Result; 

TL; DR: n’utilisez pas la version acceptée car elle est complètement cassée en ce qui concerne le traitement des caractères unicode et n’utilise jamais une API interne

J’ai effectivement trouvé un problème de double codage étrange 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:

  • les parameters de requête sont codés automatiquement à l’aide de l’indexeur NameValueCollection ( et ceci utilise UrlEncodeUnicode , pas régulier prévu UrlEncode (!) )
  • Ensuite, quand vous appelez uriBuilder.Uri il crée un nouvel Uri utilisant un constructeur qui effectue un encodage une fois de plus (encodage d’URL normal)
  • Cela ne peut pas être évité en faisant uriBuilder.ToSsortingng() (même si cela retourne Uri correct dont IMO est au moins une incohérence, peut-être un bogue, mais c’est une autre question) et en utilisant la méthode HttpClient acceptant la chaîne – le client crée toujours Uri chaîne comme ceci: new Uri(uri, UriKind.RelativeOrAbsolute)

Petit mais complet:

 var builder = new UriBuilder { Scheme = Uri.UriSchemeHttps, Port = -1, Host = "127.0.0.1", Path = "app" }; NameValueCollection query = HttpUtility.ParseQuerySsortingng(builder.Query); query["cyrillic"] = "кирилиця"; builder.Query = query.ToSsortingng(); 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 ssortingngUri = builder.ToSsortingng(); // returns more 'correct' (still `UrlEncodedUnicode`, but at least once, not twice) new HttpClient().GetSsortingngAsync(ssortingngUri); // this creates Uri object out of 'ssortingngUri' 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.ToSsortingng() + httpClient.GetSsortingngAsync(ssortingng) ou uriBuilder.Uri + httpClient.GetSsortingngAsync(Uri) vous httpClient.GetSsortingngAsync(Uri) par envoyer un paramètre codé en double

Exemple fixe pourrait être:

 var uri = new Uri(builder.ToSsortingng(), dontEscape: true); new HttpClient().GetSsortingngAsync(uri); 

Mais cela utilise un constructeur Uri obsolète

PS sur mon dernier .NET sur Windows Server, le constructeur Uri avec le commentaire bool doc dit “obsolète, dontEscape est toujours faux”, mais fonctionne réellement comme prévu (saute lors de l’évasion)

Donc ça ressemble à un autre bug …

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

Mise à jour: encore une fois, NameValueCollection ne fait qu’UrlEncodeUnicode, qui n’est plus censé être utilisé et est incompatible avec url.encode / decode standard (voir NameValueCollection to URL Query? ).

L’ NameValueCollection query = HttpUtility.ParseQuerySsortingng(builder.Query); est donc: ne jamais utiliser ce hack avec la NameValueCollection query = HttpUtility.ParseQuerySsortingng(builder.Query); car cela gâchera vos parameters de requête unicode. Construisez simplement la requête manuellement et assignez-la à UriBuilder.Uri .

Premier exemple de se faire mal en utilisant un code qui n’est pas censé être utilisé comme ça

Vous voudrez peut-être consulter Flurl [divulgation: I’m the author], un générateur d’URL fluide avec une bibliothèque compagnon facultative qui l’étend dans un client REST 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(); 

Consultez les documents pour plus de détails. Le package complet est disponible sur NuGet:

PM> Install-Package Flurl.Http

ou juste le générateur d’URL autonome:

PM> Install-Package Flurl

Dans un projet ASP.NET Core, vous pouvez utiliser la classe QueryHelpers.

 // using Microsoft.AspNetCore.WebUtilities; var query = new Dictionary { ["foo"] = "bar", ["foo2"] = "bar2", // ... }; var response = await client.GetAsync(QueryHelpers.AddQuerySsortingng("/api/", query)); 

Darin a proposé une solution intéressante et intelligente, et voici une autre option:

 public class ParameterCollection { private Dictionary _parms = new Dictionary(); public void Add(ssortingng key, ssortingng val) { if (_parms.ContainsKey(key)) { throw new InvalidOperationException(ssortingng.Format("The key {0} already exists.", key)); } _parms.Add(key, val); } public override ssortingng ToSsortingng() { var server = HttpContext.Current.Server; var sb = new SsortingngBuilder(); 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.ToSsortingng(); } } 

et donc en l’utilisant, vous pouvez le faire:

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

Ou simplement en utilisant mon extension Uri

Code

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

Usage

 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

La bibliothèque de modèles d’URI RFC 6570 que je développe est capable d’effectuer cette opération. Tout le codage est géré pour vous conformément à cette 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 1.0 stable est que la documentation ne répond pas entièrement à mes attentes (voir les numéros 17 , 18 , 32 et 43 ).

Vous pouvez soit construire une chaîne de requête seule:

 UriTemplate template = new UriTemplate("{?params*}"); var parameters = new Dictionary { { "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 { { "param1", "value1" }, { "param2", "value2" }, }; Uri baseAddress = new Uri("http://www.example.com"); Uri relativeUri = template.BindByName(baseAddress, parameters); 

Comme je dois réutiliser ce petit nombre de fois, je suis venu avec cette classe qui aide simplement à décrire comment la chaîne de requête est composée.

 public class UriBuilderExt { private NameValueCollection collection; private UriBuilder builder; public UriBuilderExt(ssortingng uri) { builder = new UriBuilder(uri); collection = System.Web.HttpUtility.ParseQuerySsortingng(ssortingng.Empty); } public void AddParameter(ssortingng key, ssortingng value) { collection.Add(key, value); } public Uri Uri{ get { builder.Query = collection.ToSsortingng(); 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 retournera l’URI: http://example.com/?foo=bar%3c%3e%26-baz&bar=second

Vous pouvez toujours utiliser IEnterprise.Easy-HTTP car il dispose d’un générateur de requêtes intégré:

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

Merci à “Darin Dimitrov”, ce sont les méthodes d’extension.

  public static partial class Ext { public static Uri GetUriWithparameters(this Uri uri,Dictionary 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 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(); } } 

Ceci est ma solution pour .Net Core basée sur la réponse de Roman Ratskey. Le type NameValueCollection a été supprimé dans .Net Core.

Code

 public static class UriExtensions { public static ssortingng AttachParameters(this ssortingng uri, Dictionary parameters) { var ssortingngBuilder = new SsortingngBuilder(); ssortingng str = "?"; foreach (KeyValuePair parameter in parameters) { ssortingngBuilder.Append(str + parameter.Key + "=" + parameter.Value); str = "&"; } return uri + ssortingngBuilder; } } 

Usage

  var parameters = new Dictionary(); parameters.Add("Bill", "Gates"); parameters.Add("Steve", "Jobs"); ssortingng uri = "http://www.example.com/index.php".AttachParameters(parameters); 

Résultat

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

Pour éviter le problème de double codage décrit dans la réponse de taras.roshko et pour garder la possibilité de travailler facilement avec les parameters de requête, vous pouvez utiliser uriBuilder.Uri.ParseQuerySsortingng() au lieu de HttpUtility.ParseQuerySsortingng() .

Je n’ai pas trouvé de meilleure solution que la création d’une méthode d’extension pour convertir un dictionnaire en QuerySsortingngFormat. La solution proposée par Waleed AK est également bonne.

Suivez ma solution:

Créez la méthode d’extension:

 public static class DictionaryExt { public static ssortingng ToQuerySsortingng(this Dictionary dictionary) { return ToQuerySsortingng(dictionary, "?"); } public static ssortingng ToQuerySsortingng(this Dictionary dictionary, ssortingng startupDelimiter) { ssortingng result = ssortingng.Empty; foreach (var item in dictionary) { if (ssortingng.IsNullOrEmpty(result)) result += startupDelimiter; // "?"; else result += "&"; result += ssortingng.Format("{0}={1}", item.Key, item.Value); } return result; } } 

Et eux:

 var param = new Dictionary { { "param1", "value1" }, { "param2", "value2" }, }; param.ToQuerySsortingng(); //By default will add (?) question mark at begining //"?param1=value1&param2=value2" param.ToQuerySsortingng("&"); //Will add (&) //"&param1=value1&param2=value2" param.ToQuerySsortingng(""); //Won't add anything //"param1=value1&param2=value2"