ASP.NET Web API: Validando con Global Action Filters

En esta cuarta entrega sobre ASP.NET Web API veremos como aplicar validaciones a nivel global del proyecto. Si observamos la forma en que manejamos los errores del modelo en el método de acción Post(), podemos identificar un patrón común para todas las validaciones:

Validación del modelo

Validación del modelo

Por lo tanto, porque no gestionarlo de manera global,y despreocuparnos de este tema. ASP.NET MVC nos proporciona un mecanismo para esto, y son los Global Action Filters que nos permiten ejecutar lógica antes o después de un método de acción (sobre este tema ya habíamos hablamos en este post).

Momento de definir el filtro de acción global que llamaremos ValidationActionFilter:

Definición del filtro de acción ValidationActionFilter

Definición del filtro de acción ValidationActionFilter

Acto seguido, registrar el filtro de acción en el método Application_Start() del archivo Global.asax:

Registrando el filtro de acción

Registrando el filtro de acción

Finalmente limpiamos el método de acción Post():

Método de acción Post()

Método de acción Post()

Probemos nuevamente el formulario, y las validaciones deberían actuar de la misma forma:

Validaciones del formulario

Validaciones del formulario

Espero que les haya servido y en próximos post vamos a continuar con más características de ASP.NET Web API.

Anuncios

ASP.NET Web API – Aplicando Validaciones

En esta tercera entrega acerca de ASP.NET Web API veremos como podemos aplicar validaciones dentro de nuestros servicios. Al igual que con ASP.NET MVC, Web API utiliza el Defaul Model Binder para realizar las validaciones, lo cual es una excelente noticia para quienes trabajamos con el framework.

Arranquemos!… el primer paso será agregar los atributos de validación a la clase Cliente, utilizando la librería DataAnnotations para tal fin:

Aplicando los atributos de validación al modelo

Aplicando los atributos de validación al modelo

Hecho esto, tenemos que ambas propiedades del modelo serán “requeridas” (obligatorias) y que el nombre de usuario tendrá como máximo 4 caracteres de longitud. Aclaración: agregué un nuevo atributo – User – a modo de hacer un poco mas completo el ejemplo.

Ahora viene lo interesante, vayamos al método de acción Post() y agreguemos la lógica necesaria para validar que los datos del cliente que llegan del servicio sean correctos:

Método de acción Post() redefinido

Método de acción Post() redefinido

Como verán, hicimos algunos cambios respecto a como a la definición del método de acción Post() de la entrada anterior. Ahora, previo a la creación del cliente, tenemos que validar que el modelo sea correcto – ModelState.IsValid – y en el caso de serlo, agregar de alguna manera los errores a la respuesta HTTP para notificarle al cliente. La forma de hacerlo es consultando las propiedades del ModelState que posean errores para recuperar los “mensajes de error” de las mismas. Luego creamos un objeto del tipo JsonArray – recuerden incluir el namespace System.Json – al cual agregar esta información. Una vez hecho todo esto, armamos la respuesta HTTP que vamos a devolver, incluyendo el código de estado HTTP 400 – Bad Request (el cual vamos a usar para identificar cuando la validación fue incorrecta) y los mensajes de error serealizados.

Momento de ir a la vista y agregar: un nuevo campo de texto para permitir la carga del nombre de usuario y un div donde vamos a visualizar los errores que pueden producirse en la creación del cliente:

Modificando la vista

Modificando la vista

Llego el momento de trabajar en el script, de forma tal que procese el resultado de las validaciones en caso de que las mismas no hayan sido correctas. Lo primero será modificar la función crearCliente():

Modificando la función crearCliente() del script Clientes.js

Modificando la función crearCliente() del script Clientes.js

Como pueden observar, uno de los cambios que hicimos en la función fue agregar una nueva función de callback en los parámetros. Esta nueva función llamada callbackFail se invocará solamente cuando ocurran errores en la validación del modelo, es decir, cuando el valor del código de estado – statusCode – devuelto por el servicio sea HTTP 400 – Bad Request. Para trabajar con los errores, debemos consultar a la propiedad responseText que recibimos en el objeto xhr – del tipo XmlHttpRequest – devuelto por el servicio (es importante serializarla antes de enviarla a la función de callback).

Pueden inspeccionar el resultado del servicio, cuando el modelo no es correcto, y observar la propiedad responseText con el arreglo de los errores:

Resultado del servicio cuando el modelo no es correcto

Resultado del servicio cuando el modelo no es correcto

Ok, llego el momento de modificar el evento click del botón “Crear”:

Modificando el evento click del botón "Crear"

Modificando el evento click del botón “Crear”

Bien, en este punto simplemente lo que hacemos es recibir el array con los mensajes de error y “dibujarlos” en el div que creamos en la vista para tal fin. Queda en ustedes ver la mejor forma de visualizar los errores :).

Probemos lo hecho:

Consumiendo los servicios desde nuestra app

Consumiendo los servicios desde nuestra app

Resumiendo, podemos ver que el manejo de las validaciones no difiere demasiado de la forma en que lo veníamos haciendo en los proyectos ASP.NET MVC. Simplemente tenemos que modificar el formato en que vamos ha devolver las mismas adaptándonos a los estándares REST.

En próximos post vamos a seguir viendo otras características de Web API.

ASP.NET Web API paso a paso

En el post anterior, hicimos una introducción al estilo de arquitectura REST y la forma de implementarlo utilizando ASP.NET Web API, un tipo de proyecto nuevo que nos provee ASP.NET MVC 4. En esta oportunidad vamos a seguir hablando de ASP.NET Web API y la forma de consumir nuestros servicios REST desde una aplicación web. La invocación a los mismos lo haremos por medio de llamadas AJAX utilizando la librería de Javascript jQuery.

En el ejemplo, construiremos una única página en la cual concentraremos todas las operaciones CRUD que se pueden realizar sobre los clientes (por si no lo recuerdan, el modelo utilizado en el post anterior fue precisamente la entidad Cliente). Es importante resaltar, antes de continuar, que en un mismo proyecto pueden convivir sin problemas una aplicación web ASP.NET MVC y los servicios REST implementados con Web API, por lo que no será necesario generar otro proyecto para tal fin.

A trabajar!… el primer paso será agregar  una nueva vista, que vamos a llamar GestionClientes. Para esto agregamos el método de acción GestionClientes() en el controller HomeController:

Creando el método de acción GestionClientes

Creando el método de acción GestionClientes

Acto seguido, crear la vista correspondiente. Para eso hacemos click derecho sobre el método de acción GestionClientes() y seleccionamos la opción “Add View“. En el dialogo que se nos abre elegimos Razor como ViewEngine y la Master Page que viene por defecto en el proyecto:

Agregando la vista GestionClientes

Agregando la vista GestionClientes

Si vamos a la definición de la master page, nos vamos a encontrar con que ya tenemos registrados todos los archivos de scripts de la carpeta Scripts gracias al sistema de compactación y minimización de scripts y CSS que nos provee ASP.NET MVC 4 (por lo que no hará falta agregar la referencia a las librería de jQuery que necesitamos utilizar):

Caching de bundles

Caching de bundles

En caso de no utilizar la master page por defecto o simplemente están trabajando sobre un documento HTML, agregar la referencia a la librería jQuery (en el ejemplo estamos utilizando la versión jquery-1.6.2.js).

El próximo paso es crear el script JS donde estará la lógica desde la cual vamos a realizar las llamadas AJAX a nuestro servicio REST. Dentro de la carpeta Scripts, agregamos un nuevo ítem JScript File llamado Clientes.js:

Agregando el script Clientes.js

Agregando el script Clientes.js

Para terminar esta primer parte, modificamos la vista GestionClientes.cshtml con la siguiente estructura (no me hago responsable del diseño de la misma :)):

Modificando la vista ClientesGestion

Modificando la vista ClientesGestion

Con este paso ya tenemos todo preparado para empezar. Para seguir un cierto orden en el desarrollo, vamos a ir trabajando con cada una de las operaciones CRUD que habíamos definido en el post anterior.

GET – Obtener la representación de una la lista de clientes

En esta primer operación, la intención es poder recuperar la lista de todos los clientes para visualizarlos en la página. Revisemos la entrada correspondiente:

RECURSO MÉTODO OPERACIÓN URI
Lista de Clientes GET Obtener la representación de una la lista de clientes. http://localhost:%5Bport%5D/Api/Clientes/

Muchos ya se habrán dado cuenta de que lo más simple de hacer es una llamada AJAX (utilizando lógicamente el verbo HTTP GET) y recuperar la lista de clientes para visualizarlos de alguna manera en la página… y están en lo correcto, así que manos a la obra!

El primer paso es actualizar el método de acción Get() del controlador ClientesController:

Definiendo el método de acción Get()

Definiendo el método de acción Get()

En este caso no vamos a trabajar contra una base de datos, ya que la intención del post es mostrar la forma de invocar los servicios REST, por lo que vamos a crear una propiedad en el controlador con una lista de objetos Cliente emulando un simplísimo repositorio de datos.

El segundo paso es escribir la lógica necesaria para recuperar y visualizar los clientes en la página. Para eso vamos a agregar lo siguiente dentro del archivo de script Clientes.js:

Script para recuperar los clientes

Script para recuperar los clientes

Momento de comentarles un poco sobre el código. Lo que hicimos fue crear la función recuperarClientes() desde la cual realizamos la petición AJAX para invocar al método Get() del servicio REST utilizando la función $.getJSON() que nos provee jQuery. A la función $.getJSON() le pasamos dos parámetros: el primero es la URL a la cual se hará la llamada, y el segundo una función de callback que se ejecutará una vez que este lista la respuesta del servidor. La función de callback recibirá en una colección de objetos – clientes – JSON el cual vamos a recorrer para armar el HTML que se mostrará en la página (pueden notar que en este caso la negociación cliente-servidor determina que la respuesta no sea un XML, como en el post anterior, sino que sea en formato JSON).

Si ejecutamos la aplicación, y entramos a la página recién creada, veremos la lista de clientes obtenidas desde el servicio ;). Es importante entender que la llamada al servicio se hizo por medio del verbo HTTP GET, y solo tuvimos que especificar la URL del servicio – ‘/api/clientes/’ – sin necesidad de especificar el nombre del método que necesitábamos (internamente el servidor mapeará la petición realizada con el verbo HTTP GET – y sin parámetros -, con el método de acción Get() del controlador ClientesController).

Recuperando y visualizando la lista de clientes

Recuperando y visualizando la lista de clientes

GET – Obtener la representación de un cliente

La segunda operación con la que vamos a trabajar es la búsqueda de un cliente por el Id. Repasemos la operación correspondiente:

RECURSO MÉTODO OPERACIÓN URI
Cliente GET Obtener la representación de un cliente. http://localhost:%5Bport%5D/Api/Clientes/5

A diferencia del método anterior, puede ocurrir que devolvamos un resultado, o que invoquemos al servicio con un Id de un cliente inexistente. En este último caso se debería informar al usuario de dicha situación. Como los servicios web basados en REST intentan trabajar de manera similar al protocolo HTTP, podemos hacer uso de los códigos de estado HTTP  para informa acerca de estos casos. Por ejemplo, podríamos utilizar el código de estado HTTP 404 Not Found si se intenta recuperar un cliente que no existe, y un código de estado HTTP 500 Internal Server Error si ocurrió un error en el procesamiento (este último en realidad ocurre sin necesidad de especificarlo).

Aclarado esto, trabajemos sobre nuestro el método de acción correspondiente:

Definiendo el método de acción Get(int id)

Definiendo el método de acción Get(int id)

Modificamos la vista:

Modificando la vista ClientesGestion

Modificando la vista ClientesGestion

Ahora, es el momento de trabajar nuevamente sobre nuestro script:

Agregando lógica al script Clientes.js

Agregando lógica al script Clientes.js

Veamos rápidamente el código que agregamos al scritp. Antes que nada definimos el evento click del botón “Buscar”  – ver la parte final del código-. En dicho evento vamos a recuperar el Id del cliente que el usuario ingreso en la caja de texto con el atributo Id=“IdCliente” – selector $(‘#IdCliente’). Luego llamamos a la función buscarCliente(id, callback) pasándole además del Id del cliente, una función de callback que se ejecutará en caso de que la búsqueda sea exitosa. Esta función de callback recibirá como parámetro nuestro un objeto cliente – serealizado en  formato JSON – que usaremos para armar la porción de HTML que visualizaremos en la página.

La función buscarCliente(id, callback) recibe como parámetro el Id del Cliente que queremos recuperar, y a partir de ese dato hace la invocación AJAX al servicio. Dentro de los parámetros vamos ha indicar: la URL del servicio – url:‘/api/clientes/’-,  los parámetros que espera el servicio – { id : id } – (el cual va permitir al servidor identificar cual de los dos métodos GET llamar), el verbo HTTP – type:“GET” – y el formato de respuesta eperado – contentType:“application/json;charset=utf-8″.

Un punto importante a tener en cuenta es la forma en que vamos a procesar la respuesta del servicio. Un buen recurso que nos provee la función $.ajax de jQuery es que podemos invocar distintas funciones a partir del código de estado – “statusCode” – que retorne el servicio! 🙂 En este caso, si el código de estado HTTP es 200 OK, invocamos a la función de callback para visualizar la información por pantalla. Por el contrario si el código de estado HTTP es 404 Not Found, le podemos informar al usuario que no se encontró el cliente. También podríamos procesar el código de estado HTTP 500 Internal Server Error cuando ocurran problemas del lado del servidor para notificar al usuario (lo dejo como tarea para el hogar).

Veamos un ejemplo cuando buscamos un cliente que existe:

Búsqueda de clientes en la vista

Búsqueda de clientes en la vista

Ahora un ejemplo intentando buscar un cliente inexistente:

Cliente no encontrado!

Cliente no encontrado!

POST – Crear un cliente

Ahora llego el momento de poder crear un nuevo cliente. Inspeccionemos la entrada:

RECURSO MÉTODO OPERACIÓN URI
Cliente POST Crear un cliente. http://localhost:%5Bport%5D/Api/Clientes/{cliente}

Empecemos con el método de acción, el cual va a tener la siguiente estructura:

Definiendo el método de acción Post

Definiendo el método de acción Post

Respecto de las operaciones anteriores, la definición de este método es algo diferente. Pueden ver que en este caso, el parámetro que recibe es el cliente que queremos crear (veremos mas adelante como enviarlo). La respuesta también es algo diferente a lo que veníamos trabajando. En este caso, luego de que hayamos creamos el nuevo recurso (cliente) vamos a armar el mensaje de respuesta HTTP que viajará en el encabezado de la misma y que contendrá, entre otras cosas, la ubicación del nuevo recurso (el cual se encuentra bajo la URI “/api/clientes/idNuevoCliente”) y el código de estado que corresponde a dicha operación: HTTP 201 Created (HttpStatusCode.Created).

En la vista vamos a agregar lo siguiente:

Actualizando la vista GestionClientes

Actualizando la vista GestionClientes

Momento de trabajar nuevamente en el script:

Actualizando el script Clientes.js

Actualizando el script Clientes.js

En cuanto a la asignación al evento “click” del botón “Crear cliente” es muy similar a la que hicimos en la búsqueda de clientes. La función que vamos a invocar es crearCliente(nuevoCliente, callback) y la función de callback que le pasaremos mostrará en una ventana modal el Id del nuevo cliente. En esta caso la función no espera un Id, lo que  debemos pasarle es un objeto javascript con la estructura de la entidad Cliente, que luego será serealizado y enviado al servicio.

Dentro de la función crearCliente, lo que vamos a hacer es una invocación AJAX, utilizando el verbo HTTP POST (la cual se mapeará con el método Post() del servicio). En los parámetros – data – vamos a enviar los datos del cliente aplicándole la función JSON.stringify, la cual nos va a permitir serializar nuestro objeto javascript a JSON string. El resto es historia conocida, solo que este caso nos importa el código de estado HTTP 201 Create para invocar a la función de callback.

Vamos a crear el cliente:

Creando un cliente

Creando un cliente

Cargamos el nombre del cliente y presionamos “Crear”:

Cliente creado existosamente

Cliente creado existosamente

POST – Actualizar un cliente

La próxima operación con la que vamos a trabajar en la actualización de un cliente. Revisemos la tabla:

RECURSO MÉTODO OPERACIÓN URI
Cliente POST Actualizar un cliente. http://localhost:%5Bport%5D/Api/Clientes/{cliente}

Como vemos, el verbo HTTP que vamos a utilizar en esta operación es PUT. Trabajemos entonces en el método de acción correspondiente:

Definiendo el método de acción Put()

Definiendo el método de acción Put()

A diferencia del método POST, este no retorna ningún resultado. Solamente en el caso de que el cliente no exista, devolveremos el código de estado HTTP 404 Not Found.

En la vista agregamos lo siguiente:

Modificando la vista GestionClientes

Modificando la vista GestionClientes

Y agregamos al script la lógica necesaria para poder actualizar un cliente:

Actualizamos el script Clientes.js

Actualizamos el script Clientes.js

Lo que hicimos es muy similar a la creación de un cliente. El cambio más importante, es que la llamada la hacemos utilizando el verbo HTTP PUT.

Ejemplo de la edición de un cliente:

Editando un cliente

Editando un cliente

DELETE – Eliminar un cliente

La última operación en la que vamos a trabajar es la eliminación de un cliente. Revisemos la tabla:

RECURSO MÉTODO OPERACIÓN URI
Cliente DELETE Eliminar un cliente. http://localhost:%5Bport%5D/Api/Clientes/5

Trabajemos sobre el método de acción correspondiente a esta operación:

Definiendo el método de acción Delete()

Definiendo el método de acción Delete()

La lógica del método de acción es bastante simple, el primer paso es validar que el cliente que intentamos eliminar exista (de lo contrario retornamos un mensaje con el código de estado HTTP 404 Not Found). De ser afirmativo, realizamos la eliminación propiamente dicha y retornamos un mensaje con el código de estado HTTP 204 No Content.

En la vista agregamos lo siguiente:

Modificando la vista GestionClientes

Modificando la vista GestionClientes

Modifiquemos por última vez el script:

Modificando el script Clientes.js

Modificando el script Clientes.js

A esta altura, no habrá notado nada raro en esta implementación ;). Probemos:

Eliminando un cliente

Eliminando un cliente

Resumiendo, la intención de este post fue mostrarles como podemos realizar un simple AMB de clientes utilizando Web API para exponer nuestros servicios REST y jQuery para consumirlos desde el cliente. También mostrarle la importancia de los verbos HTTP para describir las distintas operaciones que podemos realizar sobre los recursos, y no quedarnos simplemente en el uso de los verbos GET o POST para tal fin.

En próximos post vamos a ver como integrar Web API con el mecanismo de routing, el soporte de Model Bindings, Validations y Actions Filters y como integrar OData.