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(..))
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
.
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;
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 utiliseUrlEncodeUnicode
, pas prévu régulierUrlEncode
(!) ) - puis, quand vous appelez
uriBuilder.Uri
il crée de nouveauxUri
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 correctUri
qui IMO est au moins incohérence, peut-être un bug, mais c'est une autre question) et ensuite en utilisantHttpClient
méthode accepter chaîne de caractères - client crée encoreUri
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
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
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));
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;
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
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);
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
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();
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();
}
}
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
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()
.
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();
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¶m2=value2"
param.ToQueryString("&"); //Will add (&)
//"¶m1=value1¶m2=value2"
param.ToQueryString(""); //Won't add anything
//"param1=value1¶m2=value2"