Aplicaciones Universales: Cómo consumir un servicio REST

Hola developers!

Antes de empezar, investiguemos por qué es un importante el uso de un Web API o un servicio web. La explicación es muy sencilla: No es posible para un dispositivo móvil conectarse a una base de datos directamente. Por lo tanto, necesitamos a alguien que nos sirva de intermediario para que se logre la comunicación entre la BD y el celular... y ese es el servicio web.

Muy bien, para entender mejor como funciona todo esto, crearemos una mini aplicación! :D

En este caso, la aplicación que construiremos será una muy parecida a la que viene por defecto en nuestros dispositivos llamada Tiempo, pero en versión Dummies :P. Utilizaremos la geolocalización de los dispositivos para averiguar nuestra ubicación exacta y con estos datos, le pediremos a nuestro servicio que nos mande la información del clima en ese preciso lugar.

Como ya suponen, es hora de abrir Visual Studio 2015 y crear una nueva aplicación en blanco.

NuevaAplicacionWebAPI

Obteniendo acceso a la API

El servicio web que usaremos es el de OpenWeatherMaps.org. Esta web nos proporciona una API para manejar datos del clima en distintas partes del mundo.

Una vez que entramos al link, debemos registrarnos donde dice Sign Up y llenar el formulario. Una vez registrados, nos proporcionarán un API key, que utilizaremos más adelante.

Hay una serie de suscripciones pagadas que nos dan una mayor cantidad de características y demás, pero para nuestro propósito solo utilizaremos la versión free.

Ahora vamos a la sección API dentro de la página y eligiremos la opción de Current Weather data. A continuación veremos ejemplos de cómo hacer llamadas a su servicio. Hay varias formas de hacerlo, pero en nuestro caso usaremos el formato de coordenadas de ubicacion (lat y lon) más o menos así:

api.openweathermap.org/data/2.5/weather?lat={lan}&lon={lon}

Para que funcione tenemos que agregarle nuestro API key al final (&appid=[tuPropioAPIkey]) y reemplazar {lan} y {lon} por coordenadas reales para que nos muestre un resultado. Además, podemos pedirle que la temperatura la obtengamos en Celsius (&units=metric) y la información en español (&lang=es). Quedaría algo así:

api.openweathermap.org/data/2.5/weather?lat=-8.1333&lon=-79.0356&appid=2de143494c0b295cca9337e1e96b00e0&units=metric&lang=es

Si accedemos al link de arriba verán que nos muestra un texto en formato JSON, que será el texto que en realidad leerá nuestra aplicación.

{"coord":{"lon":-79.03,"lat":-8.12},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base":"cmc stations","main":{"temp":297.15,"pressure":1010,"humidity":78,"temp_min":297.15,"temp_max":297.15},"wind":{"speed":5.7,"deg":150},"clouds":{"all":75},"dt":1451941200,"sys":{"type":1,"id":4404,"message":0.0086,"country":"PE","sunrise":1451905434,"sunset":1451950723},"id":3691175,"name":"Trujillo","cod":200}

Preparándonos para deserializar

Para poder deserializar JSON en nuestra aplicación debemos construir un modelo de clases. Esto podemos hacerlo manualmente, pero existe una herramienta mucho más sencilla que hace el trabajo por nosotros y se llama Json2CSharp. Solo debemos copiar el JSON que nos generó el servicio de OpenWeatherMap en el espacio en blanco y clic en Generate. Nos debe devolver algo como esto:


public class Coord
{
    public double lon { get; set; }
    public double lat { get; set; }
}

public class Weather
{
    public int id { get; set; }
    public string main { get; set; }
    public string description { get; set; }
    public string icon { get; set; }
}

public class Main
{
    public double temp { get; set; }
    public int pressure { get; set; }
    public int humidity { get; set; }
    public double temp_min { get; set; }
    public double temp_max { get; set; }
}

public class Wind
{
    public double speed { get; set; }
    public int deg { get; set; }
}

public class Clouds
{
    public int all { get; set; }
}

public class Sys
{
    public int type { get; set; }
    public int id { get; set; }
    public double message { get; set; }
    public string country { get; set; }
    public int sunrise { get; set; }
    public int sunset { get; set; }
}

public class RootObject
{
    public Coord coord { get; set; }
    public List<Weather> weather { get; set; }
    public string @base { get; set; }
    public Main main { get; set; }
    public Wind wind { get; set; }
    public Clouds clouds { get; set; }
    public int dt { get; set; }
    public Sys sys { get; set; }
    public int id { get; set; }
    public string name { get; set; }
    public int cod { get; set; }
}

La clase principal (o la que engloba a todas las demás clases) es RootObject (si quieres le puedes cambiar de nombre). Esta será nuestro clase objetivo a la hora de deserializar.

El siguiente paso es copiar dicho código a nuestro proyecto. Nos vamos a Project > Add Class y le ponemos de nombre OpenWeatherMapProxy.cs (o como ustedes crean conveniente).

AgregarClaseWebAPI

En nuestra clase OpenWeatherMapProxy agregaremos el siguiente método que hará la magia:


public async static Task<RootObject> GetWeather(double lat, double lon)
{
    var http = new HttpClient();

    //    Ingresa aquí tu URL manteniendo el {0} y {1} 
    //    que recibirá los parámetros de localización del dispositivo
    var url = String.Format("http://api.openweathermap.org/data/2.5/weather?lat={0}&lon={1}&appid=7c7fe02f69d0cfa8130a1ed83985af28&units=metric&lang=es", lat, lon);

    //  Obtiene JSON a partir del API de OpenWeatherMap.Org
    var response = await http.GetAsync(url);
    var result = await response.Content.ReadAsStringAsync();

    //  Deserializa el JSON a un objeto de tipo RootObject
    var serializer = new DataContractJsonSerializer(typeof(RootObject));

    //  Necesario para este tipo de operaciones
    var ms = new MemoryStream(Encoding.UTF8.GetBytes(result));
    var data = (RootObject)serializer.ReadObject(ms);

    return data;
}

Para que funcione este código debemos agregar un paquete NuGet dando clic derecho a nuestra solución > Manage NuGet Packages y buscamos Microsoft.Net.Http.

ManageNugetWebAPI

Lo instalamos e importamos las librerías que necestiamos agregando los siguientes using al inicio de nuestra clase:


using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.IO;

Ahora, para asegurarnos que nuestro serializador funcione correctamente, vamos a agregar una sintaxis especial para ello. A cada clase generada por nuestra herramienta web Json2CSharp le pondremos el "título" [DataContract] y a cada propiedad le pondremos [DataMember], de tal forma que quede como a continuación:


[DataContract]
public class Coord
{
    [DataMember]
    public double lon { get; set; }

    [DataMember]
    public double lat { get; set; }
}

[DataContract]
public class Weather
{
    [DataMember]
    public int id { get; set; }

    [DataMember]
    public string main { get; set; }

    [DataMember]
    public string description { get; set; }

    [DataMember]
    public string icon { get; set; }
}

Y así sucesivamente...
Esto le ayudará a nuestro serializador a reconocer cuáles son los objetos y cuáles son sus propiedades.

Accediendo a la geolocalización

Debido a que esta es una característica que requiere del uso del hardware de los dispositivos, necesitamos advertir al usuario de esto para que nos den los permisos requeridos y que nuestra aplicación funcione correctamente. Así que tendremos que buscar el archivo Package.appxmanifest en el Solution Explorer, clic en la pestaña Capabilities, buscamos la opción Location y le damos clic.

AppManifestWebAPI

Ahora podemos cerrar dicho archivo (no se olviden de guardar) y agregar una nueva clase que llamaremos LocationManager.cs (ya saben, Project > Add Class).

Dentro de la clase generada copiamos el siguiente método:


public async static Task<Geoposition> GetPosition()
{
    //  Nos informa si el usuario aceptó el uso de su GPS
    var accessStatus = await Geolocator.RequestAccessAsync();
    if (accessStatus != GeolocationAccessStatus.Allowed)
    {
        //  Mejorable
        throw new Exception();
    }

    //  Ninguna precisión específica requerida
    var geolocator = new Geolocator { DesiredAccuracyInMeters = 0 };

    //  Posición real actual
    var position = await geolocator.GetGeopositionAsync();

    return position;
}

De igual manera, debemos importar la siguiente librería:


using Windows.Devices.Geolocation;

Desarrollando la interfaz

Ahora nos vamos a MainPage.xaml para crear nuestra interfaz. Dentro de la etiqueta <Grid> copiemos el siguiente código:


<StackPanel VerticalAlignment="Center">
    <Image Name="TempImage" 
            Width="200" 
            Height="200" 
            HorizontalAlignment="Center"/>
    <TextBlock Name="TempTextBlock" 
            FontSize="40" 
            HorizontalAlignment="Center"/>
    <TextBlock Name="DescTextBlock" 
            FontSize="32" 
            HorizontalAlignment="Center"/>
    <TextBlock Name="LocationTextBlock" 
            FontSize="30" 
            HorizontalAlignment="Center"/>
</StackPanel>

Este será nuestra inferfaz: una imagen representativa del clima actual y tres bloques de texto que mostrarán la temperatura, una breve descripción del clima y el nombre de la ciudad de ubicación del dispositivo. Pueden hacerle cualquier cambio si desean, depende de ustedes!

Hagamos funcionar nuestra app!

El último paso que nos queda es manejar un evento que se dispare al momento de cargar la app y haga todo el proceso para que finalmente veamos todo nuestro trabajo reflejado en nuestra interfaz. Ready? GO!

Dentro de MainPage.xaml, en la etiqueta <Page> agregamos un nuevo namespace escribiendo Loaded="Page_Loaded" al final.

XamlPageLoadedWebAPI

De modo que quede así:


<Page
    x:Class="WeatherAPI.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WeatherAPI"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Loaded="Page_Loaded">

Esto nos permitirá usar un método en nuestro archivo MainPage.xaml.cs para que toda la lógica funcione a penas abrimos la app. El método que usaremos es el siguiente:


private async void Page_Loaded(object sender, RoutedEventArgs e)
{
    var position = await LocationManager.GetPosition();

    RootObject myWeather = await OpenWeatherMapProxy.GetWeather(
        position.Coordinate.Latitude,
        position.Coordinate.Longitude);

    //  Muestra el ícono representativo del clima (OPCIONAL)
    string icon = String.Format("http://openweathermap.org/img/w/{0}.png", myWeather.weather[0].icon);
    TempImage.Source = new BitmapImage(new Uri(icon, UriKind.Absolute));

    //  Muestra la temperatura, descripción y ubicación
    TempTextBlock.Text = ((int)myWeather.main.temp).ToString() + "°C";
    DescTextBlock.Text = myWeather.weather[0].description;
    LocationTextBlock.Text = myWeather.name;
}

Y finalmente importaremos lo siguiente:


using Windows.UI.Xaml.Media.Imaging;

No se preocupen por las líneas verdes, aparentemente las librerías han cambiado para UWP, pero aún funcionan por ahora.

Muy bien, es hora de testear la app!
Les debe salir un pequeño mensaje que les pregunta si quieren dar acceso a la ubicación. Deben aceptar y les debe mostrar algo como esto :D

FinalApp

Terminamos!

Tenemos nuestra aplicación lista y corriendo. Solo para recalcar que hay algunas partes en que el código se puede optimizar y que además se le puede agregar muchas más características, pero eso ya queda en ustedes, programadores :).

No duden en preguntarme cualquier cosa, yo aquí estaré siempre con buena voluntad. Saludos!

Código de Github: