domingo, 18 de mayo de 2014

Implementar Servicio REST Parte III

Ahora continuaremos con la implementación del servicio que hemos creado desde las entradas anteriores, puedes acceder a nuestra entada anterior desde aquí.

Pues bien ahora vamos a desarrollar las peticiones faltantes, para ello agregaremos las siguientes lineas a nuestro archivo Clientes.js:

$(document).ready(function() {
    function recuperarClientes() {
        $.ajax({
            url: '/api/clientes',
            type: "GET",
            data: { },
            success: function (data) {
                if (data != null) {
                    var target = $('#clientes');
                    target.empty();
                    for (var i = 0; i < data.length; i++) {
                        target.append('<li>'+ '('+data[i].Id+') '+data[i].Nombre + ' '+data[i].Apellido+'</li>' );
                    }
                }
                else {
                    alert("No se pudo obtener informacion mediante AJAX");
                }
            }
        });
    }

    function buscarCliente(codigo) {
        $.ajax({
            url: '/api/clientes/',
            type: "GET",
            data: {id:codigo},
            success: function (data) {
                if (data != null) {
                    var target = $('#clientes');
                    target.empty();
                    target.append('<li>' + '(' + data.Id + ') ' + data.Nombre + ' ' + data.Apellido + '</li>');
                }
                else {
                    alert("No se pudo obtener informacion mediante AJAX");
                }
            }
        });
    }


    function crearCliente(nuevoCliente) {
        $.ajax({
            url: "/api/clientes/",
            data: JSON.stringify(nuevoCliente),
            type: "POST",
            contentType: "application/json;charset=utf-8",
            success: function(data) {
                if (data != null) {
                    alert("El nuevo cliente ha sido creado con el Id " + data.Id);
                    recuperarClientes();
                } else {
                    alert("No se ha podido almacenar al cliente, por favor intentelo nuevamente");
                }
            }
        });
    }

    function actualizarCliente(cliente, callback) {
        $.ajax({
            url: "/api/clientes",
            data: JSON.stringify(cliente),
            type: "PUT",
            contentType: "application/json;charset=utf-8",
            statusCode: {
                200: function () {
                    callback();
                    recuperarClientes();
                },
                404: function() {
                    alert("Cliente no encontrado!");
                }
            }
        });
    }

function eliminarCliente(id) {
        $.ajax({
            url: "/api/clientes/" + id,
            data: {},
            type: "DELETE",
            success: function () {
                recuperarClientes();
                alert("Se ha eliminado al cliente");
            }
        });
    }

    recuperarClientes();
    $('#buscarCliente').click(function() {
        var id = $('#IdCliente').val();
        if (id != null) {
            buscarCliente(id);
        } else {
            alert('No existe el cliente');
        }
    });

    $('#crearCliente').click(function () {
        var nuevoCliente = {
            Nombre: $("#NombreCliente").val(),
            Apellido: $("#ApellidoCliente").val()
        };
        crearCliente(nuevoCliente);
    });


    $("#actualizarCliente").click(function() {
        var cliente = {
            Id: $("#Id").val(),
            Nombre: $("#NombreClienteM").val(),
            Apellido: $("#ApellidoClienteM").val()
        };
        actualizarCliente(cliente, function() {
            alert("El Cliente se ha actualizado... En la db el cambio si seria persistente");
        });
    });
  
    $("#eliminarCliente").click(function () {
        var id = $('#IdEliminar').val();
        eliminarCliente(id);
    });
});



Con estas modificaciones en el Script podremos utilizar los verbos PUT, DELETE y POST. Completando nuestro ejemplo. Ahora daremos vida a estas peticiones en nuestro servicio REST, actualizando nuestro archivo ClientesController con el siguiente código:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Remoting;
using System.Web;
using System.Web.Http;
using System.Web.Http.Description;
using Microsoft.Ajax.Utilities;
using WebApiRest.Models;

namespace WebApiRest.Controllers
{
    public class ClientesController : ApiController
    {
        private List<Cliente> clientes = new List<Cliente>
        {
            new Cliente() {Id = 1,Nombre = "Luis",Apellido = "Chavez"},
            new Cliente() {Id = 2,Nombre = "Juan",Apellido = "Perez"},
            new Cliente() {Id = 3,Nombre = "Antonio",Apellido = "Fuentes"}
        };
      //GET /api/clientes
        public IEnumerable<Cliente> Get()
        {
            return clientes;
        }

        //GET /api/clientes/1
        public Cliente Get(int id)
        {
            var cliente = clientes.SingleOrDefault(c => c.Id == id);
            if (cliente != null)
            {
                return cliente;
            }
            return null;
        }

        //POST /api/clientes/5/Luis
        [ResponseType(typeof(Cliente))]
        public IHttpActionResult Post(Cliente nuevoCliente)
        {
            nuevoCliente.Id = clientes.Select(c => c.Id).Max() + 1;
            clientes.Add(nuevoCliente);
            //return Ok(clientes);
            return CreatedAtRoute("DefaultApi", new { id = nuevoCliente.Id }, nuevoCliente);
        }

        //PUT /api/clientes/5/Luis
        public HttpResponseMessage Put(Cliente clienteUpdate)
        {
            var cliente = clientes.SingleOrDefault(c => c.Id == clienteUpdate.Id);
            if (cliente != null)
            {
                cliente.Nombre = clienteUpdate.Nombre;
                cliente.Apellido = clienteUpdate.Apellido;
                return new HttpResponseMessage(HttpStatusCode.OK);
            }
                throw new HttpResponseException(HttpStatusCode.NotFound);
        }

        //DELETE /api/clientes/5
        [ResponseType(typeof(Cliente))]
        public HttpResponseMessage Delete(int id)
        {
            var cliente = clientes.SingleOrDefault(c => c.Id == id);
            if (cliente != null)
            {
                clientes.Remove(cliente);
                return new HttpResponseMessage(HttpStatusCode.OK);
            }
            throw  new HttpResponseException(HttpStatusCode.NotFound);
        }
    }
}


Ahora agregaremos el interfaz necesario para poder enviar la información de las peticiones JQuery al servicio REST, quedando la vista de la siguiente manera:


@{
    ViewBag.Title = "GestionClientes";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="@Url.Content("~/Scripts/Clientes.js")" type="text/javascript"></script>
<h2>Gestion de Clientes</h2>
<p id="clientes"></p>
<h2>Buscar Clientes</h2>
@Html.TextBox("IdCliente")
<input type="button" id="buscarCliente" value="Buscar"/>
<h2>Crear cliente</h2>
@Html.Label("Nombre:")
@Html.TextBox("NombreCliente")
@Html.Label("Apellido:")
@Html.TextBox("ApellidoCliente")
<input type="button" id="crearCliente" value="Crear" />
<h2>Actualizar cliente</h2>
@Html.Label("Id:")
@Html.TextBox("Id")
@Html.Label("Nombre:")
@Html.TextBox("NombreClienteM")
@Html.Label("Apellido:")
@Html.TextBox("ApellidoClienteM")
<input type="button" id="actualizarCliente" value="Actualizar" />
<h2>Eliminar cliente</h2>
@Html.Label("Id:")
@Html.TextBox("IdEliminar")
<input type="button" id="eliminarCliente" value="Eliminar" />



La modificación de la vista anterior nos dará una salida como la siguiente:




Cuando tengamos esto listo, entonces debemos recompliar nuestro proyecto y procederemos a ejecutarlo y a realizar las pruebas que muestran las imágenes a continuación:

Crear Cliente


Actualizar Cliente




Eliminar Cliente



Bien ahora hemos terminado de construir nuestro servicio REST, con esto tenemos clara la base de funcionamiento de este tipo de servicio. En la siguiente entrega modificaremos este ejemplo terminado para que la información sea consistente en una base de datos.


domingo, 11 de mayo de 2014

Implementar Servicio REST Parte II

Ahora continuaremos con la implementación de nuestro servicio REST, sin embargo si no has visto la primera parte del post te recomiendo que lo hagas aquí para poder entrar en contexto de lo que haremos a continuación.

Pues teniendo en cuenta lo que desarrollamos en el ejemplo anterior, hemos corroborado que nuestro código fuente es funcional, por lo que explicaremos su funcionamiento y su implementación.



   private List<Cliente> clientes = new List<Cliente>
    {
        new Cliente() {Id = 1,Nombre = "Luis",Apellido = "Chavez"},
        new Cliente() {Id = 2,Nombre = "Juan",Apellido = "Perez"},
        new Cliente() {Id = 3,Nombre = "Antonio",Apellido = "Fuentes"}
    };

   //GET /api/clientes
   public IEnumerable<Cliente> Get()
   {
      return clientes;
   }


Pues bien, primero que vemos es una lista que simulará nuestro repositorio de datos, que cuenta con 3 clientes que corresponden a nuestra entidad Cliente que creamos en nuestra entrega anterior. Posteriormente encontramos el método Get que corresponderá al verbo HTTP que habíamos comentado y que nos permitirá hacer una petición para que se nos entregue la lista de clientes y esto lo probamos en nuestra entrega anterior en el navegador, sin embargo aunque esto es posible no queremos consumirlo de esta manera, pero entonces se preguntarán ¿Cómo lo consumiremos?.

Pues bien lo primero que haremos es desviarnos un poco de nuestro controlador para poder ver las rutas de nuestra aplicación, similar a un proyecto MVC, para ello abrimos el archivo Global.asax y nos muestra un método que se ejecuta al inicio de nuestra aplicación que carga las configuraciones de nuestro sitio, entre ellas se encuentran las rutas de nuestro API, entonces ahora vamos a nuestra tabla de rutas que encontraremos en el archivo RouteConfig.cs que se encuentra dentro de la carpeta App_Start y dentro de este archivo encontraremos lo siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace WebApiRest
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}


Acá podemos ver que nuestra tabla de rutas nos muestra la misma configuración que podemos encontrar en cualquier aplicacion MVC de ASP.NET, en donde invocamos a nuestros métodos de acción el controlador al que pertenece según el estándar. Esto es lo que permite que se lance nuestra aplicación en el Home que mostramos en la entrega anterior, pero aún no explica como es que se pudo acceder al método ClientesController si en nuestro navegador digitamos "/api/clientes", pues bien en este tipo de aplicaciones se definen las rutas de las peticiones aparte, ya que este archivo de rutas manejará el sitio general y en el archivo que se mostrará a continuación las rutas del servicio REST y esto lo encontramos en el archivo WebApiConfig.cs y encontramos lo siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;

namespace WebApiRest
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional}
                // /{name}
                //,name=UrlParameter.Optional
            );
        }
    }
}


Pues acá encontramos la razón del porque nuestra aplicación fue capaz de resolver la ruta "/api/clientes" y esto es porque se define el mapeo de la ruta del api de esta manera, sin embargo si lo deseamos podríamos personalizarla igual que la de otra aplicación MVC pero por estandar se suele dejar de esta manera, pero tal como comentaba en la entrega anterior si se desea nombrar a los métodos de manera diferente al estándar debemos modificar la ruta para que se le especifique la acción ya que por defecto si la dejamos como se encuentra entonces debemos utilizar los verbos HTTP ya que solamente de esa manera el servicio REST entendería las peticiones que se hagan. En nuestro ejemplo trabajaremos con el estandár.

Pues bien ahora de esta breve explicación continuaremos con la implementación para poder consumir nuestro primer método que corresponde al verbo GET, para ello debemos crear el método "GestionClientes" en el controlador Home, con el siguiente código:

public ActionResult GestionClientes()
{
   return View();
}


Como podemos ver la acción solamente nos regresa una vista, sin embargo esta por el momento no existe, por lo que si invocamos el URI "/home/GestionClientes" obtendremos un error y para ello entonces crearemos una vista nueva dando clic derecho sobre el subdirectorio Home que se encuentra dentro del directorio Views y seleccionamos el menú Add -> View y creamos la vista GestionClientes, que corresponde al método de acción que hemos creado, como lo muestra la siguiente imagen:


Como pueden notar hemos seleccionado un Layout(equivalente al master page de Web Forms) para que se incluyan los archivos de CSS que dan el mismo estilo a la página principal. Ahora damos clic en Add par aregar la nueva vista a nuestro proyecto y debería tener un contenido similar a este:

@{
    ViewBag.Title = "GestionClientes";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>GestionClientes</h2>


En esta vista debemos implementar las peticiones a nuestro servicio REST, pero ¿Cómo lo haremos?. Pues lo haremos mediante JQuery. Para ello incluiremos un archivo javascript en nuestro directorio "Scripts", el archivo tendrá el nombre "Clientes.js".

Ahora entonces estamos listos para implementar nuestro servicio, lo primero que haremos es desarrollar una función jquery que nos permita ejecutar el primero verbo HTTP, que sería GET para obtener la lista de los clientes, que corresponde a la lista que creamos en nuestro controlador de clientes, tal como lo muestra el siguiente código:

$(document).ready(function() {
    function recuperarClientes() {
        $.ajax({
            url: '/api/clientes',
            type: "GET",
            data: { },
            success: function (data) {
                if (data != null) {
                    var target = $('#clientes');
                    target.empty();
                    for (var i = 0; i < data.length; i++) {
                        target.append('<li>'+ '('+data[i].Id+') '+data[i].Nombre + ' '+data[i].Apellido+'</li>' );
                    }
                }
                else {
                    alert("No se pudo obtener informacion mediante AJAX");
                }
            }
        });
    }

recuperarClientes();
});


En nuestro archivo JQuery hemos declarado la función de carga del documento en la cual se ejecutará la función "recuperarClientes()" que también se encuentra declarada en el cuerpo de la función de carga del documento, esto hará una petición AJAX a nuestro controlador, como se puede ver la url "/api/clientes" que es la ruta que hemos declarado para acceder a nuestro recurso y el tipo es una petición GET que corresponderá al método Get() que hemos declarado, se puede observar que en nuestro Web Api hemos podido sobrecargar este método ya que existe otro método con el nombre Get pero que necesita un parámetro entero, entonces justamente esta es la clave la petición negocia con el servicio REST el método GET que corresponde a la firma que no necesita parámetros, ya que "data: { }" se encuentra vacío, ya que no necesita ningún parámetro. Y ahora hacemos la inclusión de este script, modificando la vista de la siguiente manera:

@{
    ViewBag.Title = "GestionClientes";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="@Url.Content("~/Scripts/Clientes.js")" type="text/javascript"></script>
<h2>Gestion de Clientes</h2>
<p id="clientes"></p>

Al ejecutar nuestro proyecto deberíamos tener una salida como esta:



Si hemos tenido esta salida en nuestro navegador entonces hemos hecho correctamente todos los pasos expuestos. Ahora entonces agregaremos a nuestra vista una caja de texto y un botón para buscar un cliente específico mediante su ID, para utilizar y mostrar que nuestro servicio REST entiende que método debe ejecutarse, modificando nuestra vista de la siguiente manera:

@{
    ViewBag.Title = "GestionClientes";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="@Url.Content("~/Scripts/Clientes.js")" type="text/javascript"></script>
<h2>Gestion de Clientes</h2>
<p id="clientes"></p>
<h2>Buscar Clientes</h2>
@Html.TextBox("IdCliente")
<input type="button" id="buscarCliente" value="Buscar"/>


Y obtendríamos la siguiente salida en nuestra vista:



Ahora entonces debemos agregar la función buscarCliente a nuestro archivo javascript y posteriormente hacer que cuando hagamos submit al botón Buscar se ejecute dicha función, agregando el siguiente código:

function buscarCliente(codigo) {
        $.ajax({
            url: '/api/clientes/',
            type: "GET",
            data: {id:codigo},
            success: function (data) {
                if (data != null) {
                    var target = $('#clientes');
                    target.empty();
                    target.append('<li>' + '(' + data.Id + ') ' + data.Nombre + ' ' + data.Apellido + '</li>');
                }
                else {
                    alert("Código inexistente!");
                }
            }
        });
    }

$('#buscarCliente').click(function() {
        var id = $('#IdCliente').val();
        if (id != null) {
            buscarCliente(id);
        } else {
            alert('No existe el cliente');
        }
    });


Ahora digitemos cualquiera de los tres códigos disponibles y obtendríamos en pantalla la muestra de ese cliente en específico y los demás desaparecerían. Así mismo si se digitamos un código que no existe obtendríamos un alert, tal como se muestran en las siguientes pantallas:


En la siguiente entrega terminaremos de abordar la implementación de nuestro servicio con los verbos PUT, DELETE y POST. Completando nuestro ejemplo.

sábado, 10 de mayo de 2014

Implementar Servicio REST Parte I

En esta oportunidad vamos a aprender a implementar un servicio REST sencillo para entender como funciona esta tecnología y posteriormente en una siguiente entrega convertiremos este mismo ejemplo aplicando Entity Framework la consistencia de los datos.
Pues bien lo primero que debemos entender que significa REST y para ello nos basaremos en la explicación que nos brinda Wikipedia:

La Transferencia de Estado Representacional (Representational State Transfer) o REST es una técnica de arquitectura software para sistemas hipermedia distribuidos como la World Wide Web. El término se originó en el año 2000, en una tesis doctoral sobre la web escrita por Roy Fielding, uno de los principales autores de la especificación del protocolo HTTP y ha pasado a ser ampliamente utilizado por la comunidad de desarrollo.
Si bien el término REST se refería originalmente a un conjunto de principios de arquitectura —descritos más abajo—, en la actualidad se usa en el sentido más amplio para describir cualquier interfaz web simple que utiliza XML y HTTP, sin las abstracciones adicionales de los protocolos basados en patrones de intercambio de mensajes como el protocolo de servicios web SOAP. Es posible diseñar sistemas de servicios web de acuerdo con el estilo arquitectural REST de Fielding y también es posible diseñar interfaces XMLHTTP de acuerdo con el estilo de llamada a procedimiento remoto (RPC), pero sin usar SOAP. Estos dos usos diferentes del término REST causan cierta confusión en las discusiones técnicas, aunque RPC no es un ejemplo de REST.
Los sistemas que siguen los principios REST se llaman con frecuencia RESTful.

REST afirma que la web ha disfrutado de escalabilidad como resultado de una serie de diseños fundamentales clave:
  • Un protocolo cliente/servidor sin estado: cada mensaje HTTP contiene toda la información necesaria para comprender la petición. Como resultado, ni el cliente ni el servidor necesitan recordar ningún estado de las comunicaciones entre mensajes. Sin embargo, en la práctica, muchas aplicaciones basadas en HTTP utilizan cookies y otros mecanismos para mantener el estado de la sesión (algunas de estas prácticas, como la reescritura de URLs, no son permitidas por REST)
  • Un conjunto de operaciones bien definidas que se aplican a todos los recursos de información: HTTP en sí define un conjunto pequeño de operaciones, las más importantes sonPOSTGETPUT y DELETE. Con frecuencia estas operaciones se equiparan a las operaciones CRUD en bases de datos (ABMC en castellano: Alta, Baja, Modificación y Consulta) que se requieren para la persistencia de datos, aunque POST no encaja exactamente en este esquema.
  • Una sintaxis universal para identificar los recursos. En un sistema REST, cada recurso es direccionable únicamente a través de su URI.
  • El uso de hipermedios, tanto para la información de la aplicación como para las transiciones de estado de la aplicación: la representación de este estado en un sistema REST sontípicamente HTML o XML. Como resultado de esto, es posible navegar de un recurso REST a muchos otros, simplemente siguiendo enlaces sin requerir el uso de registros u otra infraestructura adicional.

Pues entonces en base a la explicación que hemos tomado de Wikipedia procederemos entonces a entender como aplicarlo. El ejemplo se desarrollará en VS2012 con la versión  3 de Razor:

Lo primero que haremos será crear un proyecto dando clic en el menú File -> New -> Project y crearemos una aplicación Web MVC 4 con el nombre AppWebApiRest, como se muestra en la siguiente imagen:



Posteriormente debemos seleccionar el tipo de proyecto que deseamos realizar y debemos seleccionar una proyecto Web API como lo muestra la siguiente imagen y damos clic en "Ok":



Entonces el proyecto se generará con la siguiente estructura:


Como podemos observar en el árbol de directorios del proyecto parece un proyecto cualquiera de MVC pues se mantiene la misma estructura de directorios, ya que recuerden que la tecnología que usamos aplica como estándar el patrón. A continuación haré una breve explicación de los directorios más importantes:

  • App_Start: Este directorio contiene la configuración inicial de nuestra aplicación, más adelante explicaremos el contenido de los archivos que utilizaremos de esta carpeta.
  • Content: En este directorio se alojan los temas, css y cualquier contenido estático que deseemos utilizar en nuestro proyecto. Recuerden que esto no es una camisa de fuerza podemos crear nuestros archivos en otros directorios, pero nosotros nos basaremos en el standar, esto para que todo sea más sencillo en nuestro proceso de aprendizaje.
  • Controllers: En este directorio se ubicarán los controladores que deseemos crear en nuestra aplicación MVC. Siempre siguiendo el standar acá es donde lo crearemos. Recuerden que por defecto se nos crearon dos controladores, mostraremos el contenido pero solamente utilizaremos el HomeController de los que se nos han creado por defecto.
  • Scripts: En este directorio se incluyen todos los scripts que por defecto el IDE considera que son necesarios para incluir en nuestra aplicación(por el tipo seleccionado). Pues entonces acá también podemos agregar los scripts que generemos en nuestro caso generaremos el archivo que realizará las peticiones al servicio que generaremos más adelante.
  • Views: En este directorio almacenamos las vistas que deseemos crear. En nuestro ejemplo solamente generaremos una, ya que este post será para explicar la lógica básica de la implementación de este tipo de servicios:
  • Shared: Este subdirectorio pertenece al directorio Views y en el podremos depositar cualquier vista parcial o Layout que sería el equivalente a los "Masterpages" de la tecnología de WebForms.

Ya que tenemos nuestro proyecto iniciaremos a crear la entidad que manejaremos en nuestro ejemplo, para ello debemos hacer clic derecho sobre el directorio "Models" y en el submenú seleccionaremos Add -> New item -> Class. El nombre de la clase deberá ser "Cliente" y su contenido será el siguiente:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebApiRest.Models
{
    public class Cliente
    {
        public int Id { get; set; }
        public string Nombre { get; set; }
        public string Apellido { get; set; }
    }
}


Como podemos ver esta entidad solamente posee tres propiedades, esto debido a que realizaremos un ejemplo básico donde almacenaremos solamente un ID que se autogenerará y los Nombres y Apellidos del cliente. Ya que esta aplicación pretende hacer una pequeña agenda para almacenar clientes, buscarlos, editarlos y eliminarlos en síntesis un CRUD. Sin embargo me gustaría enfatizar que por el momento esto se hará en memoria, es decir que aún no habrá consistencia de datos porque por  el momento ese no es el objetivo de esta entrega.

Pues bien, ahora debemos crear el controlador que nos permitirá interactuar entre la vista y el modelo. Para ello debemos dar clic derecho sobre el directorio "Controllers" y seleccionar la opción Add -> New item ->Web API Controller Class.

Siguiendo el estándar de MVC tendrá el nombre ClientesController, cuando se agregue el código veremos que esta clase deriva de la clase ApiController y no de la clase Controller como un controlador de las aplicaciones comunes de MVC.

Su contenido sería el siguiente:


En el podemos observar la definición de métodos, pero estos métodos son diferentes a lo que podemos observar en un controlador típico de una aplicación MVC. Justamente es por eso que deriva de una clase distinta porque su comportamiento es distinto al de un controlador de una aplicación común. Y en base a lo que pudimos extraer de wikipedia y otros medios podemos concluir lo siguiente para explicar el porque de los cambios de un controlador estándar:

  • Al derivarse de esta nueva clase se les permite los métodos de acción que se encuentren dentro de la clase no retornar necesariamente vistas, es decir que su tipo de retorno sea un ActionResult o ViewResult, sino devolver los datos puros, sin ningún tipo de formato mediante JSON, XML, WFC o cualquier otra tecnología mediante la cual se negocie (entre cliente y servidor) con el servicio REST cuando la implementación solicite la información.
  • Así mismo esta tecnología por convención respeta que los nombres de los métodos de acción sean los mismos que los verbos HTTP (GET,PUT,DELETE Y POST), evitando el uso de etiquetas para indicar que método debe ejecutar la acción. Sin embargo se pueden nombrar de una manera diferente y se le debe anteponer una etiqueta indicando el verbo al que responderá o bien indicar en la declaración de las rutas que responderá a una ruta como en una aplicación MVC estándar {controlador}/{acción}, aunque este no es el funcionamiento regular de este tipo de aplicaciones, pero puede ser una forma personalizada de hacerlo si es que así nos sentimos más cómodos. Posteriormente explicaremos la definición de las rutas, si no te encuentras familiarizado por el momento no te preocupes. Ejemplo de lo que implica cada uno de los verbos:



Ahora entonces debemos eliminar el contenido de la clase "ClientesController" y lo reemplazaremos por lo siguiente:

namespace WebApiRest.Controllers
{
    public class ClientesController : ApiController
    {
        private List<Cliente> clientes = new List<Cliente>
        {
            new Cliente() {Id = 1,Nombre = "Luis",Apellido = "Chavez"},
            new Cliente() {Id = 2,Nombre = "Juan",Apellido = "Perez"},
            new Cliente() {Id = 3,Nombre = "Antonio",Apellido = "Fuentes"}
        };
      //GET /api/clientes
        public IEnumerable<Cliente> Get()
        {
            return clientes;
        }

        //GET /api/clientes/1
        public Cliente Get(int id)
        {
            var cliente = clientes.SingleOrDefault(c => c.Id == id);
            if (cliente != null)
            {
                return cliente;
            }
            return null;
        }

        //POST /api/clientes/5/Luis
        [ResponseType(typeof(Cliente))]
        public IHttpActionResult Post(Cliente nuevoCliente)
        {
            nuevoCliente.Id = clientes.Select(c => c.Id).Max() + 1;
            clientes.Add(nuevoCliente);
            //return Ok(clientes);
            return CreatedAtRoute("DefaultApi", new { id = nuevoCliente.Id }, nuevoCliente);
        }

        //PUT /api/clientes/5/Luis
        public HttpResponseMessage Put(Cliente clienteUpdate)
        {
            var cliente = clientes.SingleOrDefault(c => c.Id == clienteUpdate.Id);
            if (cliente != null)
            {
                cliente.Nombre = clienteUpdate.Nombre;
                cliente.Apellido = clienteUpdate.Apellido;
                return new HttpResponseMessage(HttpStatusCode.OK);
            }
                throw new HttpResponseException(HttpStatusCode.NotFound);            
        }

        //DELETE /api/clientes/5
        [ResponseType(typeof(Cliente))]
        public HttpResponseMessage Delete(int id)
        {
            var cliente = clientes.SingleOrDefault(c => c.Id == id);
            if (cliente != null)
            {
                clientes.Remove(cliente);
                return new HttpResponseMessage(HttpStatusCode.OK);
            }
            throw  new HttpResponseException(HttpStatusCode.NotFound);
        }
    }
}


Si no entiendes del todo el código no te preocupes que posteriormente lo explicaremos más en detalle cuando hagamos la implementación del servicio.

Si corremos en este momento el proyecto por defecto nos ejecutará la ruta por defecto al igual que cualquier otra aplicación MVC, es decir /home/index, tal como lo muestra la siguiente pantalla:


Si vemos esto y nuestro compilar no nos arroja ningún error es que hasta el momento hemos desarrollado todo lo que va de nuestro ejemplo corrrectamente. Y comprobamos el estado de uno de los métodos si digitamos "localhost:7439/api/clientes" (recuerda que el puerto seguramente será diferente en tu equipo) y  obtenemos el siguiente resultado:


Como se puede observar el resultado es un XML, esto es así porque el navegador negoció así con el servidor el resultado. Si hemos obtenido esta salida en el navegador entonces hemos realizado correctamente nuestro ejemplo. Por el momento dejaremos nuestro ejemplo hasta aquí en una entrega posterior explicaremos los métodos y su funcionamiento y su implementación.

Espero que esta introducción nos permite entender el funcionamiento básico de un servicio REST, aunque quedamos pendientes de continuar nuestro ejemplo.


Referencias:

viernes, 2 de mayo de 2014

Aprendiendo MVC3

Después de mucho tiempo sin escribir por este medio, creo que por falta de tiempo y trabajo entre otros, pero estoy tratando de retomar esta costumbre para compartir mis nuevas experiencias, ya que por cuestiones de la vida estoy emprendiendo el aprendizaje de tecnologías nuevas para mi, aunque no para la comunidad. Por lo que si alguien se ha topado con las mismas eventualidades leyendo y no ha encontrado aún una solución quiero compartirla con cualquiera que lea este blog. 

Pues bien ya después de esta larga introducción, entremos en materia. Por el momento me encuentro aprendiendo MVC como les decía y tengo de referencia el libro Apress Pro.ASP.NET MVC3 aunque a la vez voy tratando de acoplarlo a MVC4 y luego que domine esto iniciaré con MVC5, pues bien me encuentro actualmente en el capítulo 8/9. Utilizo por el momento VS2010. Para introducir un poco, el ejemplo utiliza Ninject como DI, Entity Framework para trabajar el bindeo de la base de datos SQL Express que nos instala VS por defecto. Pues bien en el capítulo 8 se crea una aplicación MVC referente a una pequeña tienda de deportes, hasta ese capitulo todo funciona siguiendo los pasos del libro, pero en el capítulo 9 se nos plantea hacer la administración del catálogo de artículos y es cuando la modificación que recomienda el libro no nos es funcional. 

 El CRUD tendría la siguiente lógica










El libro nos recomienda el siguiente código para hacer la modificación:

Paso #1. Agregar a nuestra interfaz la firma del método SaveProduct, que será utilizado para modificar o crear un nuevo artículo mediante Entity Framework

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SportsStore.Domain.Entities;

namespace SportsStore.Domain.Abstract
{
    public interface IProductRepository
    {
        IQueryable<Product> Products { get; }
        void SaveProduct(Product product);
        void DeleteProduct(Product product);
    }
}


Paso #2. Implementamos el método de la interfaz

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Activation;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using System.Data.Entity;

namespace SportsStore.Domain.Concrete
{
    public class EfProductRepository : IProductRepository
    {
        private EfDbContext _context=new EfDbContext();

        public IQueryable<Product> Products
        {
            get { return _context.Products; }
        }

        public void SaveProduct(Product product)
        {
            if (product.ProductID == 0)
            {
                _context.Products.Add(product);
            }
            _context.SaveChanges();
        }

        public void DeleteProduct(Product product)
        {
            _context.Products.Remove(product);
            _context.SaveChanges();
        }
    }

}


En este paso se hace la actualización de objeto en la base de datos, asumiendo que si el ProductID es igual a cero, quiere decir que es un nuevo producto y por lo tanto debe hacer un nuevo registro y para ello se agrega al contexto, de lo contrario debería actualizar la entidad actual. Sin embargo es aquí cuando surge un problema y es que en ningún momento se le ha dicho al contexto que debe actualizarse, por lo que nunca se hará una actualización y justamente acá fue donde surgió mi interrogante en como resolver el problema, ya que según el libro debía funcionar. Pues bien buscando un poco en la internet encontré varios métodos, sin embargo plantearé el que creo que entendí más facilmente y que de alguna manera se parece a mi antigua forma de trabajar sin la utilización de Entity Framework:

Paso #3. Actualizar la acción del controlador "AdminController" que se desarrolla dentro del ejemplo de la siguiente manera:

[HttpPost]
        public ActionResult Edit(Product product,HttpPostedFileBase image)
        {
            if (ModelState.IsValid)
            {
                //_repository.SaveProduct(product);
                Product p = _repository.Products.FirstOrDefault(x => x.ProductID == product.ProductID);
                if ((p != null) && (image!=null))
                {
                    p.ProductID = product.ProductID;
                    p.Price = product.Price;
                    p.Category = product.Category;
                    p.Description = product.Description;
                    p.Name = product.Name;
                    p.ImageMimeType = image.ContentType;
                    p.ImageData=new byte[image.ContentLength];
                    image.InputStream.Read(p.ImageData, 0, image.ContentLength);
                }
                else
                    p = product;
                _repository.SaveProduct(p);
                TempData["message"] = string.Format("{0} has been saved", product.Name);
                return RedirectToAction("Index");
            }
            else
            {
                // there is something wrong with the data values
                return View(product);
            }

        }


Básicamente lo que se ha hecho es que similar a cuando la acción carga con el método GET, buscamos el producto que coincida con el ProductID que se ha seleccionado en la lista de productos y posteriormente validamos si la entidad esta vacía, sino lo está quiere decir entonces que encontró un objeto en el planteamiento del libro solo sería necesario hacer el "SaveProduct" pasando la entidad que viene como parámetro, pero al hacer esto si no se enviara un producto nuevo como lo plantea el libro no haría el update, por lo que debemos indicarle al contexto el objeto que deseamos actualizar y entonces a esa instancia le igualamos las propiedades del objeto que es enviado debido a que actualizaremos todos los campos y seguido de esto guardamos el producto y esto entonces le indica a Entity Framework que deseo actualizar dicho producto con los valores que le envié.

Listo con estas lineas podemos hacer que el ejemplo del libro permita actualizar sin ningún problema.