> Manuales > Manual del framework ASP.NET MVC

En este artículo del manual de ASP.NET MVC vamos a hablar sobre las validaciones cruzadas y las validaciones remotas, dos mecanismos adicionales a los ya vistos para casos más específicos.

En los dos artículos anteriores del manual de ASP.NET MVC hemos visto como usar los atributos de Data Annotations para realizar validaciones y como crearnos nuestras propias validaciones tanto en servidor como en cliente.

Pero hay un escenario en el que el uso de Data Annotations no termina de encajar del todo bien: las validaciones cruzadas. Es decir cuando la validación de un campo depende del valor de otro campo. Así, un campo puede ser obligatorio solo si en otro campo se ha entrado un valor específico, o bien podemos tener dos campos mutuamente excluyentes pero que uno de los dos deba ser informado sí o sí. Seguro que se te ocurren mil ejemplos!

Técnicamente usando Data Annotations (en ASP.NET MVC 3) es posible crear este tipo de validaciones, p.ej. el atributo Compare compara el valor de dos propiedades y falla si no son iguales (se usa para el caso típico de entrar password y comprobar password en formularios de registro). Pero para escenarios más complejos que puedan involucrar varias propiedades, en casos en que sea necesario tratar al viewmodel como un todo, ASP.NET MVC proporciona un mecanismo muy sencillo y eficaz: la interfaz IValidatableObject.

IValidatableObject

Esta interfaz, que forma parte del .NET Framework 4, es usada por el runtime de ASP.NET MVC para realizar las validaciones cruzadas. Tiene un solo método llamado Validate que debe devolver una colección de resultados de validación. Su implementación es trivial:

public class UsuarioViewModel : IValidatableObject
{
public string Nombre { get; set; }
public string Password { get; set; }
public string CompararPassword { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(Nombre))
{
yield return new ValidationResult("No puede estar vacío.", new List<string> { "Nombre" });
}
if (Password != CompararPassword)
{
yield return new ValidationResult("Deben ser iguales.", new List<string> { "Password", "CompararPassword" });
}
}
}

Listado : Implementación de IValidatableObject

La clave de este método es que sólo devuelve errores. No devuelve true o false para indicar si todo ha ido bien o no. Simplemente devuelve una colección de todos los errores encontrados. Cada ValidationResult devuelto contiene el mensaje de error y (opcionalmente) una lista con los nombres de las propiedades que se han visto involucradas en este error (a nivel técnico de ASP.NET MVC podemos decir que el valor que devolvamos aquí se añade a la colección Error de todas aquellas entradas del ModelState cuya clave esté contenida en esta lista de nombres de propiedades).

Dada esta implementación de IValidatableObject si el usuario deja el nombre vacío y entra dos passwords que no coinciden, el ModelState que recibe el controlador es:

Fïjate como el primer error devuelto (que el nombre no puede estar vacío) está asociado a la colección “Errors” del elemento 0 del ModelState (cuya clave es “Nombre”). Por otro lado el error de que las passwords deben ser iguales está asociado tanto al elemento 1 (Password) y 2 (CompararPassword) del ModelState, porque en el método Validate lo hemos devuelto asociado a esas dos propiedades. Si se usan los helpers para crear formularios (que ya hemos visto en este manual) la pantalla mostrará todos los errores.

En resumen, IValidatableObject proporciona un mecanismo rápido y sencillo para realizar validaciones cruzadas en nuestros viewmodels.

Una última observación a tener presente es que ASP.NET MVC tan solo invoca el método Validate si las validaciones por atributos han pasado. Es decir, si hay un solo atributo de Data Annotations que falla, el método Validate() no es invocado.

Validaciones remotas

El último tipo de validación que nos queda por ver, es la validación remota. Esta validación es validación en cliente (por lo que como digo siempre es un tema de usabilidad y no de seguridad) y consiste en llamar usando Ajax a una función de un controlador que nos indique si los valores actuales son correctos o no. Es pues una manera efectiva y rápida de realizar en cliente validaciones que requieren acceder a recursos del servidor. P.ej. se podría validar que un nombre de un usuario no esté dado de alta. Esta validación se realizaría en cliente pero implica ejecutar un método del servidor (ya que es donde se pueden consultar todos los usuarios de la aplicación). El mecanismo de validaciones remotas de ASP.NET MVC pone muy fácil el realizar estas validaciones al encargarse automáticamente de realizar todas las llamadas Ajax.

Habilitando la validación remota

Para habilitar la validación remota sobre una propiedad de nuestro viewmodel se usa un atributo de DataAnnotations, concretamente el atributo Remote. Su uso es extremadamente sencillo:

[Remote("ValidarNombre", "Usuarios", ErrorMessage = "Nombre inválido!")]
public string Nombre { get; set; }

Listado : Uso del atributo [Remote] en una propiedad

Basta con indicarle el nombre de la acción que deberá usarse para validar (ValidarNombre), el controlador y el mensaje de error a mostrar. Opcionalmente se puede añadir un parámetro HttpMethod que es el verbo http a usar para la llamada Ajax (se asume GET si no se pone nada).

Ahora tan solo nos queda declarar la acción ValidarNombre. Dicha acción recibirá como parámetro el valor de la propiedad que se valida (Nombre en nuestro caso) y debe devolver un booleano que indique si la validación ha ido bien (true) o no (false). El único detalle es que en lugar de devolver HTML, debe devolver un objeto JSON que contenga el valor booleano. Aunque no hemos visto (todavía) como devolver algo que no sea HTML en ASP.NET MVC, devolver JSON es trivial: basta con usar el método Json y pasarle un objeto .NET que será serializado al cliente en formato JSON. Así el código queda como:

[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public ActionResult ValidarNombre(string nombre)
{
return Json(!string.IsNullOrEmpty(nombre) && "admin" != nombre && "sa" != nombre, JsonRequestBehavior.AllowGet);
}

Listado : Acción usada para validación remota

Si te preguntas para que sirve el atributo [OutputCache] con el que estamos decorando la acción es para que ASP.NET MVC envíe los headers correspondientes para que esta respuesta no pueda ser cacheada. Ya hablaremos en otro artículo sobre temas de caching en ASP.NET MVC. De momento solo comentar que es necesario que lo incluyas en todas tus acciones que uses para validaciones remotas, ya que si no ¡su respuesta podría ser cacheada!

Y listos, no es necesario hacer nada más. Ahora a medida que el usuario va entrando el nombre este se valida usando la validación remota. Sí, sí, eso es una petición http cada vez que el usuario pulsa una tecla en el campo de texto Nombre… Ten eso en cuenta cuando apliques validaciones remotas.

No profundizaremos más en el tema de validaciones remotas, si quieres hacerlo puedes leer un post de mi blog que puse hace algún tiempo al respecto: http://geeks.ms/blogs/etomas/archive/2011/01/14/asp-net-mvc3-validaci-243-n-remota.aspx

Y recuerda (a riesgo de hacerme pesado) que eso son validaciones en cliente. No son para garantizar la seguridad de tu aplicación. Son para mejorar la experiencia de usuario.

Eduard Tomàs

Apasionado de la informática, los videojuegos, rol y... la cerveza. Key Consulta...

Manual