martes, 10 de enero de 2017

Acceso a Servicios Web REST en Android (1/2)

En los dos artículos anteriores (éste y éste) del Curso de Programación Android nos hemos ocupado de describir la forma de construir un sistema formado por un servicio web SOAP que accede a una base de datos externa y una aplicación Android que, a través de este servicio, es capaz de manipular dichos datos.
En este nuevo artículo vamos a crear un sistema similar, pero esta vez haciendo uso de la otra alternativa por excelencia a la hora de crear servicios web, y no es otra de utilizar servicios web tipo REST. Las famosas APIs que publican muchos de los sitios web actualmente no son más que servicios web de este tipo, aunque en la mayoría de los casos con medidas de seguridad adicionales tales como autenticación OAuth o similares.
REST también se asienta sobre el protocolo HTTP como mecanismo de transporte entre cliente y servidor, ya veremos después en qué medida. Y en cuanto al formato de los datos transmitidos, a diferencia de SOAP, no se impone ninguno en concreto, aunque lo más habitual actualmente es intercambiar la información en formato XML o JSON. Ya que en el caso de SOAP utilizamos XML, en este nuevo artículo utilizaremos JSON para construir nuestro ejemplo.
También vamos a utilizar un framework distinto para construir el servicio, aunque seguiremos haciéndolo en Visual Studio y en lenguaje C#. En este caso, en vez de utilizar ASP.NET a secas, vamos a utilizar el framework específico ASP.NET MVC 3, cuyo sistema de direccionamiento se ajusta mejor a los principios de REST, donde cada recurso [en nuestro caso cada cliente] debería ser accesible mediante su propia URL única. Podéis descargar MVC3 desde su página oficial de Microsoft.
En este primer artículo sobre servicios REST vamos a describir la construcción del servicio web en sí, y dedicaremos un segundo artículo a explicar cómo podemos acceder a este servicio desde una aplicación Android.
Empezamos. Lo primero que vamos a hacer será crear un nuevo proyecto en Visual Studio utilizando esta vez la plantilla llamada “ASP.NET MVC 3 Web Application“, lo llamaremos “ServicioWebRest“.
nuevo-proyecto-rest
En la ventana de opciones del proyecto dejaremos todos los datos que aparecen por defecto y seleccionaremos como plantilla “Empty” para crear una aplicación vacía.
opciones-proyecto-rest
Esto debería crearnos el nuevo proyecto con la estructura de carpetas necesaria, que como veréis es bastante elaborada.  En nuestro caso vamos a crear el servicio web de forma aislada del resto de la aplicación web, y para ello lo primero que vamos a hacer es añadir una nueva Area al proyecto, a la que llamaremos por ejemplo “Api“, lo que nos creará una estructura de carpetas similar a la de la aplicación principal pero dentro de una carpeta independiente. Esto nos permite aislar todo el código y recursos de nuestro servicio web del resto de la aplicación web (que en nuestro caso no existirá porque no es el objetivo de este artículo, pero que podríamos crear sin problemas si lo necesitáramos).
menu-nueva-area
Con esto, la estructura de nuestro proyecto será la siguiente:
estructura-proyecto-mvc3
Una vez que ya tenemos preparada toda la estructura de nuestro proyecto empecemos a añadir los elementos necesarios. Lo primero que vamos a crear será una nueva clase Cliente, igual que hicimos en el ejemplo anterior con SOAP. La colocaremos en la carpeta “Api/Models” y el código es el mismo que ya vimos:
1
2
3
4
5
6
7
8
9
namespace ServicioWebRest.Areas.Api.Models
{
    public class Cliente
    {
        public int Id { get; set; }
        public string Nombre { get; set; }
        public int Telefono { get; set; }
    }
}
El siguiente elemento a añadir será una nueva clase que contenga todas las operaciones que queramos realizar sobre nuestra base de datos de clientes. Llamaremos a la clase ClienteManager. En este caso sí vamos a añadir las cuatro operaciones básicas sobre clientes, y una adicional para obtener el listado completo, de forma que más tarde podamos mostrar la implementación en Android de todos los posibles tipos de llamada al servicio. Los métodos que añadiremos serán los siguientes:
  • Cliente ObtenerCliente(int id)
  • List<Clientes> ObtenerClientes()
  • bool InsertarCliente(Cliente c)
  • bool ActualizarCliente(Cliente c)
  • bool EliminarCliente(int id)
Los dos primeros métodos nos servirán para recuperar clientes de la base de datos, tanto por su ID para obtener un cliente concreto, como el listado completo que devolverá una lista de clientes. Los otros tres métodos permitirán insertar, actualizar y eliminar clientes a partir de su ID y los datos de entrada (si aplica). El código de todos estos métodos es análogo a los ya implementados en el caso de SOAP, por lo que no nos vamos a parar en volverlos a comentar, tan sólo decir que utilizan la api clásica de ADO.NET para el acceso a SQL Server. En cualquier caso, al final del artículo tenéis como siempre el código fuente completo para poder consultar lo que necesitéis. A modo de ejemplo veamos la implementación de los métodos ObtenerClientes() e InsertarCliente().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public bool InsertarCliente(Cliente cli)
{
      SqlConnection con = new SqlConnection(cadenaConexion);
 
      con.Open();
 
      string sql = "INSERT INTO Clientes (Nombre, Telefono) VALUES (@nombre, @telefono)";
 
      SqlCommand cmd = new SqlCommand(sql,con);
 
      cmd.Parameters.Add("@nombre", System.Data.SqlDbType.NVarChar).Value = cli.Nombre;
      cmd.Parameters.Add("@telefono", System.Data.SqlDbType.Int).Value = cli.Telefono;
 
      int res = cmd.ExecuteNonQuery();
 
      con.Close();
 
      return (res == 1);
}
 
public List<Cliente> ObtenerClientes()
{
      List<Cliente> lista = new List<Cliente>();
 
      SqlConnection con = new SqlConnection(cadenaConexion);
 
      con.Open();
 
      string sql = "SELECT IdCliente, Nombre, Telefono FROM Clientes";
 
      SqlCommand cmd = new SqlCommand(sql,con);
 
      SqlDataReader reader =
            cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
 
      while (reader.Read())
      {
          Cliente cli = new Cliente();
 
          cli = new Cliente();
          cli.Id = reader.GetInt32(0);
          cli.Nombre = reader.GetString(1);
          cli.Telefono = reader.GetInt32(2);
 
          lista.Add(cli);
      }
 
      reader.Close();
 
      return lista;
}
Hasta ahora, todo el código que hemos escrito es bastante genérico y nada tiene que ver con que nuestro proyecto sea de tipo MVC. Sin embargo, los dos siguientes elementos sí que están directamente relacionados con el tipo de proyecto que tenemos entre manos.
Lo siguiente que vamos a añadir será un controlador a nuestro servicio web. Este controlador (clase ClientesController) será el encargado de contener las diferentes acciones que se podrán llamar según la URL y datos HTTP que recibamos como petición de entrada al servicio. Para nuestro ejemplo, añadiremos tan sólo dos acciones, una primera dirigida a gestionar todas las peticiones que afecten a un único cliente (insertar, actualizar, eliminar y obtener por ID), y otra que trate la petición del listado completo de clientes. Las llamaremos Clientes() y Cliente() respectivamente. Estas acciones harán uso de una instancia de la clase ClienteManagercreada anteriormente para realizar las acciones necesarias contra la base de datos. Cada acción será también responsable de formatear sus resultados al formato de comunicación que hayamos elegido, en nuestro caso JSON.
La acción Clientes es muy sencilla, se limitará a llamar al método ObtenerClientes() y formatear los resultados como JSON. Para hacer esto último basta con crear directamente un objeto JsonResult llamado al método Json() pasándole como parámetro de entrada el objeto a formatear. Todo esto se reduce a una sola linea de código:
1
2
3
4
5
6
[HttpGet]
public JsonResult Clientes()
{
    return Json(this.clientesManager.ObtenerClientes(),
                JsonRequestBehavior.AllowGet);
}
Habréis notado también que hemos precedido el método con el atributo [HttpGet]. Para intentar explicar esto me hace falta seguir hablando de los principios de diseño de REST. Este tipo de servicios utiliza los propios tipos de petición definidos por el protocolo HTTP para diferenciar entre las operaciones a realizar por el servicio web. Así, el propio tipo de petición HTTP realizada (GETPOSTPUT o DELETE), junto con la dirección URL especificada en la llamada, nos determinará la operación a ejecutar por el servicio web. En el caso ya visto, el atributo [HttpGet] nos indica que dicho método se podrá ejecutar al recibirse una petición de tipo GET.
Entenderemos todo esto mejor ahora cuando veamos el código de la acción Cliente(). En esta acción, dependiente del tipo de petición HTTP recibida, tendremos que llamar a un método u otro del servicio web. Así, usaremos POST para las inserciones de clientes, PUT para las actualizaciones, GET para la consulta por ID y DELETE para las eliminaciones. En este caso no precedemos el método por ningún atributo, ya que la misma acción se encargará de tratar diferentes tipos de petición.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public JsonResult Cliente(int? id, Cliente item)
{
    switch (Request.HttpMethod)
    {
        case "POST":
            return Json(clientesManager.InsertarCliente(item));
        case "PUT":
            return Json(clientesManager.ActualizarCliente(item));
        case "GET":
            return Json(clientesManager.ObtenerCliente(id.GetValueOrDefault()),
                        JsonRequestBehavior.AllowGet);
        case "DELETE":
            return Json(clientesManager.EliminarCliente(id.GetValueOrDefault()));
    }
 
    return Json(new { Error = true, Message = "Operación HTTP desconocida" });
}
Algunos de vosotros seguro que os estáis preguntando cómo distinguirá el servicio cuándo llamar a la acción Clientes() para obtener el listado completo, o a la acción Cliente() para obtener un único cliente por su ID, ya que para ambas operaciones hemos indicado que se recibirá el tipo de petición http GET.
Pues bien, aquí es donde nos va a ayudar el último elemento a añadir al servicio web. Realmente no lo añadiremos, sino que lo modificaremos, ya que es un fichero que ya ha creado Visual Studio por nosotros. Se trata de la clase ApiAreaRegistration. La función de esta clase será la de dirigir las peticiones recibidas hacia una acción u otra del controlador según la URL utilizada al realizarse la llamada al servicio web.
En nuestro caso de ejemplo, vamos a reconocer dos tipos de URL. Una de ellas para acceder a la lista completa de cliente, y otra para realizar cualquier acción sobre un cliente en concreto:
  • Lista de clientes: http://servidor/Api/Clientes
  • Operación sobre cliente: http://servidor/Api/Clientes/Cliente/id_del_cliente
Cada uno de estos patrones tendremos que registrarlos mediante el método MapRoute() dentro del método RegisterArea() que ya tendremos creado dentro de la clase ApiAreaRegistration. Así, para registrar el primer tipo de URL haremos lo siguiente:
1
2
3
4
5
6
7
8
9
context.MapRoute(
    "AccesoClientes",
    "Api/Clientes",
    new
    {
        controller = "Clientes",
        action = "Clientes"
    }
);
Como primer parámetro de MapRoute() indicamos un nombre descriptivo para el patrón de URL. El segundo parámetro es el patrón en sí, que en este caso no tiene partes variables. Por último indicamos el controlador al que se dirigirán las peticiones que sigan este patrón eliminando el sufijo “Controller” (en nuestro caso será el controlador ClientesController) y la acción concreta a ejecutar dentro de dicho controlador (en nuestro caso la acción Clientes()).
Para el segundo tipo de URL será muy similar, con la única diferencia de que ahora habrá una parte final variable que se corresponderá con el ID del cliente y que asignaremos al parámetro “id” de la acción. En este caso además, dirigiremos la petición hacia la acción Cliente(), en vez de Clientes().
1
2
3
4
5
6
7
8
9
10
context.MapRoute(
    "AccesoCliente",
    "Api/Clientes/Cliente/{id}",
    new
    {
        controller = "Clientes",
        action = "Cliente",
        id = UrlParameter.Optional
    }
);
Como todo esto en cuenta, y por recapitular un poco, las posibles llamadas a nuestro servicio serán las siguientes:
GET  /Api/Clientes
Recuperará el listado completo de clientes y lo devolverá en formato JSON.
GET  /Api/Clientes/Cliente/3
Recuperará el cliente con el ID indicado en la URL y lo devolverá en formato JSON.
POST  /Api/Clientes/Cliente  { Nombre:”nombre”, Telefono:1234 }
Insertará un nuevo cliente con los datos aportados en la petición en formato JSON.
PUT  /Api/Clientes/Cliente/3  { Id:3, Nombre:”nombre”, Telefono:1234 }
Actualizará el cliente con el ID indicado en la URL con los datos aportados en la petición en formato JSON.
DELETE  /Api/Clientes/Cliente/3
Eliminará el cliente con el ID indicado en la URL.
Llegados aquí, tan sólo tenemos que ejecutar nuestro proyecto y esperar a que se abra el navegador web. En principio no se mostrará un error por no encontrar la página principal de la aplicación, ya que no hemos creado ninguna, pero nos asegurará que el servidor de prueba está funcionando, por lo que nuestro servicio debería responder a peticiones.
Así, si escribimos en la barra de direcciones por ejemplo la siguiente dirección (el puerto puede variar):
http://localhost:1234/Api/Clientes/Cliente/4
deberíamos recibir un fichero en formato JSON que contuviera los datos del cliente con ID = 4 de nuestra base de datos. Sería un fichero con contenido similar al siguiente:
{"Id":4,"Nombre":"cliente4","Telefono":4444}

No hay comentarios:

Publicar un comentario