La web es tuya con Flurl
En la actualidad la mayoría de las apps que conocemos tienen alguna interacción con internet: ya sea para realizar publicaciones a Facebook o Twitter, para recuperar información acerca del clima o el tráfico o para actualizar tu puntuación en un juego.
Es por eso que seguramente las aplicaciones que tu desarrolles también tengan que incluir esta interacción... y mientras que podrías tu mismo programar toda la infraestructura para realizar peticiones, ¿por qué no usar una librería que ya lo hace? En este post te voy a hablar de Flurl y Flurl.Http, que son librerías con una api fluida que facilitan la escritura y ejecución de peticiones HTTP.
Construcción de URLs
De entrada, podría parecerte sencillo crear URLs por tu cuenta, pero cuando menos te das cuenta resulta que olvidaste poner un '/' y todo el código está mal. Con Flurl te puedes olvidar de esos problemas:
El método AppendPathSegment
ayuda a añadir un segmento de URL a otra, al ser una api fluida, podemos encadenar varias llamadas a AppendPathSegment
:
var myCoolUrl2 = "http://localhost/"
.AppendPathSegment("sales")
.AppendPathSegment("Q1")
.AppendPathSegment("max");
Console.WriteLine(myCoolUrl2);
Al final, myCoolUrl2
es:
http://localhost/sales/Q1/max
El método devuelve un objeto del tipo Url
, sobre el que podemos trabajar y que es directamente transformable en una cadena de texto.
Si lo que necesitamos es añadir muchos segmentos a una URL, tal vez sea más conveniente hacerlo a través de un arreglo, tal vez sea mejor usar AppendPathSegments
. Este método es similar al anterior, solo que opera con un mútliples segmentos de URL que son enviados en un arreglo marcado como params:
var urlParts = new string[] { "liga_mx", "results", "america" };
var builtUrl2 = new Url("http://api.soccer-data.com");
builtUrl2.AppendPathSegments(urlParts);
Console.WriteLine(builtUrl2);
Al final, builtUrl2
es:
http://api.soccer-data.com/liga_mx/results/america
Query string
Flurl también tiene la capacidad de añadir parámetros a la query string de una URL usando el método SetQueryParams
, para lo cual se crea un objeto anónimo:
var endDate = DateTime.Today;
var startDate = endDate.AddDays(-30);
builtUrl.SetQueryParams(new { endDate, startDate, c = "concacaf champions league" });
Console.WriteLine(builtUrl);
Al final, builtUrl
es:
http://api.soccer-data.com/liga_mx/results/america?endDate=04%2F25%2F2016%2000%3A00%3A00&startDate=03%2F26%2F2016%2000%3A00%3A00&c=concacaf%20champions%20league
Como puedes darte cuenta, los nombres de las propiedades en el objeto anónimo new { endDate, startDate, c = competition }
fueron respetadas y los parámetros fueron codificados como URL para ser enviados en una petición HTTP.
También podemos quitar parámetros de la query string con RemoveQueryParams
:
builtUrl.RemoveQueryParams("c", "startDate");
Console.WriteLine(builtUrl);
Al final, builtUrl
es:
http://api.soccer-data.com/liga_mx/results/america?endDate=04%2F25%2F2016%2000%3A00%3A00
Creación de un cliente HTTP
A partir de tener una URL construida con los métodos anteriores, podemos comenzar a configurar nuestro cliente HTTP:
var client = myCoolUrl1
.WithBasicAuth("antonio", "secretPass")
.WithHeader("User-Agent", "Flurl-Sample");
Ahora, cada vez que hagamos una petición con un cliente creado por esta URL, contendrá el encabezado siguiente:
Authorization: Basic YW50b25pbzpzZWNyZXRQYXNz User-Agent: Flurl-Sample
También tenemos más opciones, como la de asignar varios encabezados a la vez:
var client2 = builtUrl
.WithOAuthBearerToken("t0k3n")
.WithHeader("Accept-Language", "it")
.WithHeader("User-Agent", "Flurl-Sample")
.WithHeaders(new
{
CustomHeader = "Another value",
Accept = "text/json"
});
Cuyo resultado es:
Authorization: Bearer t0k3n Accept-Language: it User-Agent: Flurl-Sample CustomHeader: Another value Accept: text/json
Ejecución de peticiones
Ya sea con un cliente HTTP o con una Url podemos comenzar a realizar peticiones asíncronas. Para este ejemplo, usaré la Pokéapi:
var pokemonId = 25;
var url1 = "http://pokeapi.co/api/v2/"
.AppendPathSegment("pokemon")
.AppendPathSegment(pokemonId.ToString());
Console.WriteLine("Consultando " + url1); // Consultando http://pokeapi.co/api/v2/pokemon/25
A partir de aquí podemos obtener un objeto dinámico con GetJsonAsync
:
dynamic pkmn1 = await url1.GetJsonAsync(); // pkmn1
Console.WriteLine(pkmn1.name + " " + "\nH:" + pkmn1.height + "\nW:" + pkmn1.weight + "\n");
pikachu H:4 W:60
O crear una clase que contenga las propiedades del objeto que vamos a recibir para que el resultado que tenemos sea fuertemente tipado, usando GetJsonAync<T>
:
var pokemon = await url2.GetJsonAsync<Pokemon>();
Console.WriteLine(pokemon.Name + "\nH:" + pokemon.Height + "\nW:" + pokemon.Weight + "\n");
charmander H:6 W:85
Flurl también permite hacer peticiones POST mediante PostJsonAsync
o PostUrlEncodedAsync
, además de integrar el manejo de errores mediante la FlurlHttpException
que contiene la información necesaria acerca del error sucedido, por ejemplo, mira el manejo de esta excepción
catch (FlurlHttpException ex)
{
if (ex.Call.Response != null
&& ex.Call.Response.StatusCode == HttpStatusCode.NotFound)
{
Console.WriteLine("No existe un pokemon llamado " + pokemonName);
}
}
No olvides que puedes consultar todo el código de este ejemplo en https://github.com/ThatCSharpGuy/Flurl-Sample.
Conclusión
Al principio pareciera que la construcción de URLs, configuración de clientes y ejecución de peticiones HTTP es tan sencillo que tu mismo puedes gestionarlo todo, pero depende del tamaño de lo que quieres lograr. Si tu interés es delegar esta tarea a librerías de terceros, Flurl es una gran opción y es que además de lo poco que traté de mostrar en este post, aún hay más, entre ellas la facilidad que nos brinda a la hora de hacer testing.
Instalación
Para instalarlo no hay mejor manera que el gestor de paquetes de NuGet, instala solo Flurl si quieres solo los beneficios de la construcción de URLs o Flurl.Http si quieres el paquete completo:
PM> Install-Package Flurl
PM> Install-Package Flurl.Http
O échale un ojo al proyecto en GitHub y contribuir a su desarrollo de alguna manera.