De hecho lo que vemos automáticamente al usar los helpers no son validaciones, son errores. Si introducimos una cadena alfanumérica en un campo que se ha declarado como int, cuando el Model Binder debe enlazar los datos genera un error. Los errores que el Model Binder se encuentra al intentar enlazar los valores de la request con las propiedades del viewmodel son guardados en el ModelState y pueden ser consultados desde el propio controlador y también desde la vista. De hecho, como vimos en el artículo anterior, los helpers consultan el ModelState para usar una clase CSS específica en caso de que haya algún error asociado con el campo.
Intentar convertir una cadena alfanumérica en un int es pues un error, pero hay muchos más casos en los que nos puede interesar decirle al usuario que los datos entrados son incorrectos: campos obligatorios, cadenas con un determinado formato (p.ej. un email) o dos campos que deben tener el mismo valor (p.ej. contraseña y comprobar contraseña). Son esos casos cuando nos referimos a validaciones y es lo que vamos a tratar en nuestro artículo. Pero aunque nosotros diferenciemos entre errores y validaciones ASP.NET MVC no lo hace: ambos son tratados igual, es decir ambos son guardados en el ModelState. Pero antes de profundizar más, veamos exactamente que es el ModelState.
El ModelState es básicamente un diccionario donde:
La Figura 1 muestra la composición del ModelState. La propiedad Keys contiene las claves (en este caso el viewmodel que estábamos usando era una clase con una propiedad cadena Nombre y un int llamado Edad) y la propiedad Values donde podemos ver, por cada clave, el valor que le ha sido asignado (Value) y la colección de errores (Errors).
El ModelState contiene una propiedad llamada IsValid que nos dice si el modelo es correcto, es decir si no hay errores. Esa propiedad suele usarse de la siguiente forma:
[HttpPost]
public ActionResult Index(DemoModel data)
{
if (!ModelState.IsValid)
{
return View(data);
}
else
{
// Codigo normal
return View("ok");
}
}
Si el ModelState no es válido significa que alguna entrada del usuario no es correcta, por lo tanto devolvemos de nuevo la vista que contiene el formulario con los datos. Como vimos en el artículo anterior si usamos los helpers para crear el formulario, esos mostrarán los errores (en color rojo con la CSS por defecto). Si el ModelState es válido eso significa que las entradas del usuario son correctas por lo que procedemos a realizar la acción que querramos.
Para añadir validaciones usando DataAnnotations simplemente debemos decorar la propiedad que deseemos con alguno de los atributos (algunos se encuentran en el namespace System.ComponentModel.DataAnnotations y otros en System.Web.Mvc). P.ej. si tenemos el siguiente viewmodel:
public class DemoModel
{
public string Nombre { get; set; }
public int Edad { get; set; }
}
Si queremos que el Nombre sea obligatorio podemos decorarlo con [Required]:
public class DemoModel
{
[Required]
public string Nombre { get; set; }
public int Edad { get; set; }
}
¡Y listos! Solo con añadir el atributo Required, el framework sabe que el Nombre es requerido y si el usuario no lo entra se mostrará un error:
Si comparas la Figura 2 con las capturas de pantalla del artículo anterior donde también se mostraban campos erróneos, verás una pequeña diferencia: no solo se muestra el campo de rojo, sino que también hay un mensaje de error. ¿Quién añade ese mensaje de error? Pues un helper del que todavía no habíamos hablado: Html.ValidationMessageFor. Su uso es como lo de los helpers para generar formularios: con una expresión lambda le indicamos la propiedad para la cual queremos mostrar sus mensajes de error (si los hubiese). Ese es el código completo de la vista que estamos usando:
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>DemoModel</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Nombre)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Nombre)
@Html.ValidationMessageFor(model => model.Nombre)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Edad)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Edad)
@Html.ValidationMessageFor(model => model.Edad)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
public class DemoModel
{
[Required]
public string Nombre { get; set; }
[Range(18, 65)]
public int Edad { get; set; }
}
Más atributos que existen:
public class DemoModel
{
[Required(ErrorMessage = "Nada de anónimos. ¡Aquí todo el mundo tiene un nombre!")]
public string Nombre { get; set; }
[Range(18,65, ErrorMessage = "Solo mayores de edad no jubilados")]
public int Edad { get; set; }
}
IMAGEN
La Figura 3 muestra el resultado con esos mensajes de error:
Pues bien, como no podía ser de otro modo existe un helper específico para ello, llamado Html.ValidationSummary. Este helper puede generar una lista (<li>) de elementos no ordenados (<ul>) con todas las validaciones que hayan fallado. Su uso es tan simple como añadir dicho helper en el lugar donde queramos que aparezca dicha lista. Además admite varios parámetros para personalizarlo uno de los cuales permite añadir un título que se mostrará antes de la lista:
@Html.ValidationSummary(true,"Hay varios errores:")
La cadena "Hay varios errores:" se mostrará antes de mostrar la lista. El primer parámetro por su parte indica si la lista debe mostrar todos los errores (true) o solo aquellos que no estén vinculados a ninguna propiedad (false), es decir que sean globales a todo el viewmodel (en efecto, es posible que haya errores en el ModelState que no estén asociados a ninguna propiedad en concreto).
Si por alguna razón se desea desactivar la validación en cliente en alguna vista, basta con llamar al método Html.EnableClientValidation con el parámetro a false:
@{ Html.EnableClientValidation(false); }
@using (Html.BeginForm())
{
// Codigo del form
}
Si creas atributos propios para validaciones personalizadas entontes es responsabilidad tuya asegurarte de que sean compatibles para validar en cliente (tranquilo, veremos en artículos posteriores como hacerlo), pero los que vienen, lo incluyen de serie.
[HttpPost]
public ActionResult Index(DemoModel data)
{
if (ModelState.IsValid)
{
if (Manager.ExistePersona(data))
{
ModelState.AddModelError("", "Ya existe una persona con este nombre");
}
}
if (!ModelState.IsValid)
{
return View(data);
}
else
{
// Codigo normal
return View("ok");
}
}
En esta acción, si no hay errores en el modelo (es decir los datos introducidos por el usuario son correctos) el controlador comprueba si ya existe una persona con esos datos (usando una clase inventada Manager), y si este es el caso añade un error en el ModelState. En este momento ModelState.IsValid deja de ser true (puesto que ahora hay un error). ModelState.AddModelError tiene dos parámetros:
Bueno, en este artículo nos hemos introducido en el modelo de validaciones de ASP.NET MVC. Hemos visto que es el ModelState, como usar DataAnnotations y como añadir nuestros propios errores en el ModelState. Nos han quedado varios aspectos que iremos tratando en futuros artículos como crear nuestros propios atributos de validación y ver otros mecanismos de validación que no sea usando atributos.