postasync net getasync ejemplos ejemplo async c# .net http

c# - getasync - Cadena de consulta de compilación para System.Net.HttpClient get



using httpclient postasync c# (14)

Si deseo enviar una solicitud de obtención de HTTP utilizando System.Net.HttpClient, parece que no hay API para agregar parámetros, ¿es correcto?

¿Hay alguna API simple disponible para construir la cadena de consulta que no implique construir una colección de valores de nombre y url que los codifique y finalmente concatenarlos? Esperaba usar algo como la api de RestSharp (es decir, AddParameter (..))


Si deseo enviar una solicitud de obtención de HTTP utilizando System.Net.HttpClient, parece que no hay API para agregar parámetros, ¿es correcto?

Sí.

¿Hay alguna API simple disponible para construir la cadena de consulta que no implique construir una colección de valores de nombre y url que los codifique y finalmente concatenarlos?

Por supuesto:

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

le dará el resultado esperado:

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

También puede encontrar útil la clase UriBuilder :

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

le dará el resultado esperado:

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

que podría más que alimentar con seguridad a su método HttpClient.GetAsync .


Como tengo que reutilizar este tiempo, se me ocurrió esta clase que simplemente ayuda a abstraer cómo se compone la cadena de consulta.

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

El uso se simplificará a algo como esto:

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

que devolverá el uri: http://example.com/?foo=bar%3c%3e%26-baz&bar=second


Darin ofreció una solución interesante e inteligente, y aquí hay algo que puede ser otra opción:

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

y entonces cuando lo uses, puedes hacer esto:

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


En un proyecto ASP.NET Core puede usar la clase QueryHelpers.

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


Es posible que desee comprobar Flurl [revelación: soy el autor], un generador de URL fluido con lib complementario opcional que lo extiende a un cliente REST en toda regla.

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

Consulte Flurl para obtener más detalles. El paquete completo está disponible en NuGet:

PM> Install-Package Flurl.Http

o solo el creador de URL independiente:

PM> Install-Package Flurl


Esta es mi solución para .Net Core basada en la respuesta de Roman Ratskey. El tipo NameValueCollection se eliminó en .Net Core.

Código

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

Uso

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

Resultado

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


Gracias a "Darin Dimitrov", este es el método de extensión.

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


La biblioteca de plantillas de URI de RFC 6570 que estoy desarrollando es capaz de realizar esta operación. Toda la codificación se maneja para usted de acuerdo con ese RFC. En el momento de escribir estas líneas, una versión beta está disponible y la única razón por la que no se considera una versión estable 1.0 es que la documentación no cumple completamente mis expectativas (véanse los números #17 , #18 , #32 y #43 ).

Puedes construir una cadena de consulta solo:

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

O podrías construir un URI completo:

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


No pude encontrar una solución mejor que crear un método de extensión para convertir un diccionario a QueryStringFormat. La solución propuesta por Waleed AK también es buena.

Sigue mi solución:

Crea el método de extensión:

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

Y ellos:

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"


O simplemente usando mi extensión Uri

Código

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

Uso

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

Resultado

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


Para aquellos que no desean incluir System.Web en proyectos que aún no lo usan, puede usar System.Net.Http y hacer algo como lo siguiente:

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


Para evitar el problema de codificación doble descrito en la respuesta de taras.roshko y para mantener la posibilidad de trabajar fácilmente con los parámetros de consulta, puede usar uriBuilder.Uri.ParseQueryString() lugar de HttpUtility.ParseQueryString() .


Siempre puede usar IEnterprise.Easy-HTTP porque tiene un constructor de consultas integrado:

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


TL; DR: no use la versión aceptada ya que está completamente rota en relación con el manejo de caracteres Unicode, y nunca use la API interna

De hecho, he encontrado un extraño problema de doble codificación con la solución aceptada:

Entonces, si está tratando con caracteres que necesitan ser codificados, la solución aceptada conduce a una doble codificación:

  • los parámetros de consulta se NameValueCollection utilizando el indexador NameValueCollection ( y esto usa UrlEncodeUnicode , no es un UrlEncode esperado (!) )
  • Entonces, cuando llamas a uriBuilder.Uri crea un nuevo Uri usando un constructor que codifica una vez más (codificación url normal)
  • Eso no se puede evitar haciendo uriBuilder.ToString() (aunque esto devuelve Uri correcto, que IMO es al menos incoherente, tal vez un error, pero esa es otra pregunta) y luego usando el método HttpClient aceptando cadena - el cliente todavía crea Uri de su pasado una cadena como esta: new Uri(uri, UriKind.RelativeOrAbsolute)

Pequeño, pero completo 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!

Salida:

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

Como puede ver, no importa si lo hace uribuilder.ToString() + httpClient.GetStringAsync(string) o uriBuilder.Uri + httpClient.GetStringAsync(Uri) termina enviando un parámetro codificado doble

El ejemplo fijo podría ser:

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

Pero esto usa un constructor obsoleto de Uri

PD en mi última versión de .NET en Windows Server, el constructor de Uri con bool doc comment dice "obsoleto, dontEscape siempre es falso", pero en realidad funciona como se esperaba (omite escapes)

Entonces parece otro error ...

E incluso esto es completamente incorrecto: envía UrlEncodedUnicode al servidor, no solo UrlEncoded lo que el servidor espera

Actualización: una cosa más es, NameValueCollection realmente hace UrlEncodeUnicode, que ya no se debe usar y es incompatible con url.encode / decode (ver NameValueCollection a URL Query? ).

Entonces la conclusión es: nunca use este truco con NameValueCollection query = HttpUtility.ParseQueryString(builder.Query); ya que afectará tus parámetros de consulta Unicode. Simplemente construya la consulta manualmente y asígnela a UriBuilder.Query que hará la codificación necesaria y luego obtendrá Uri usando UriBuilder.Uri .

Primer ejemplo de hacerte daño usando un código que no se supone debe usarse así