ASP.NET MVC 2: Validación de Modelos

Además del blog, podéis seguir a Scott en twitter: twitter.com/scottgu

Éste es el segundo post de una serie sobre ASP.NET MVC 2 En este post veremos algunas de las mejoras de validaciones de ASP.NET MVC 2

Validación de ASP.NET MVC2

La validación de los datos introducidos por los usuarios y reforcar las reglas de negocio es un requisito principal en la mayoría de aplicaciones web. ASP.NET MVC 2 incluye un montón de nuevas características que hacen que estas validaciones en modelos y vistas sea mucho más sencillo. Estas características están diseñadas para que la validación lógica esté siempre en el servidor, y opcionalmente podamos forzar esas validaciones en el cliente a través de JavaScript. Las infraestructuras y características de validación en ASP.NET MVC2 están diseñadas para eso:

  1. Los programadores podemos aprovechar el soporte de validación DataAnnotation de .NET Framework. Las DataAnnotations ofrecen una forma realmente sencilla de añadir declarativamente reglas de negocio a objetos y propiedades con un mínimo código.
  2. Los programadores pueden integrar incluso su propio motor de validaciones, o aprovechar frameworks existentes como Castle Validator o la librería de validación EntLib. Las características de validación de ASP.NET MVC 2 están diseñadas para que sea sencillo integrar cualquier tipo de arquitectura de validación - mientras sigue aprovechando la infraestructura de validación de ASP.NET MVC2 (incluyendo validación en cliente, validación de enlace de modelos, etc).

Esto significa que habilitar la validación es realmente sencillo para la mayoría de escenarios, mientras que al mismo tiempo es lo suficientemente flexible para escenarios más avanzados.

Habilitando validaciones con ASP.NET MVC 2 y DataAnnotations

Veamos un escenario simple CRUD con ASP.NET MVC2 que aprovecha el soporte para DataAnnotations. Concretamente implementaremos un formulario de creación que permita a un usuario introducir datos:

Queremos asegurar que la información introducida es válida ántes de guardarla en la base de datos - y también mostrar cual es el error.

Queremos que esta validación ocurra tanto en el servidor como en el cliente (vía JavaScript). También queremos asegurar que nuestro código mantiene el principio DRY ("Don't repeat yourself") - es decir, deberíamos aplicar reglas de validación en un solo sitio.

Ahora vamos a implementar el escenario anterior en VS 2010 con ASP.NET MVC 2. Podemos implementar el mismo escenario con VS 2008 y ASP.NET MVC 2

Paso 1: Implementar la clase FriendsController (sin ninguna validación)

Comenzaremos por añadir la clase "Person" a un proyecto nuevo de ASP.NET MVC2:

Tiene cuatro propiedades (implementadas con el soporte de propiedades automáticas de C#, que también se soporta en VB 2010 - woow)

Luego añadimos la controladora "FriendsController" al proyecto que expone dos métodos de acción "Create". El primer método se llama cuando se hace una petición HTTP-GET de la url /Friends/Create. Mostrará un formulario vacío para introducir los datos. El segundo método se llama cuando se realize una petición HTTP-POST a la url /friends/create. Mapea los datos a un objeto Person, comprueba que no hay errores de binding, y si es válido lo guarda en la base de datos (implementaremos el trabajo de base de datos más adelante en el tutorial). Si los datos son inválidos, el método de acción recarga la pantalla con errores:

Después de implementar nuestro controlador, hacemos clic derecho en uno de los métodos de acción y seleccionamos "Add View" - que nos mostrará el diálogo "Add View". Ahora seleccionamos generar la vista "Create" a la que se le pase un objeto de tipo Person:

Visula Studio generara la vista Create.aspx  en el directorio ViewsFriends del proyecto. Fijaos cómo aprovecha la característica de HTML helpers fuertemente tipados de ASP.NET MVC2 (permitiendo un intellisense mejorado):

Y ahora cuando ejecutamos la aplicación y vamos a la url /Friend/Create tendremos un formulario vació listo para introducir datos.

Como no hemos implementado ninguna validación en la aplicación, nada nos limita a introducir datos erroneos y mandarlos al servidor.

Paso 2: Habilitar la validación con DataAnnotations

Vamos a actualizar la aplicación para asegurar algunas reglas básicas. Implementaremos estas reglas en nuestro modelo de Person - y NO en la controladora o en la vista. Lo bueno de implementar las reglas en nuestro objeto Person es que asegurará que la validación se realizará en cualquier parte de nuestra aplicación que use un objeto de tipo Person (por ejemplo: si después añadimos un escenario de edición). Esto nos ayudará a asegurarnos de que mantenemos nuestro código DRY y evitamos repetir reglas en varias partes.

ASP.NET MVC 2 permite a los desarrolladores añadir fácilmente validaciones declarativas por medio de atributos del modelo o en las clases viewmodel, y tener todas esas validaciones cada vez que ASP.NET MVC realiza operaciones de binding en la aplicación. Para ver esto en acción, vamos a actualizar nuestra clase Person para que tenga unas cuantas validaciones. Para ello añadiremos un "using" del namespace "System.ComponentModel.DataAnnotations" - y decoraremos las propiedades de Person con los atributos [Required], [StringLength], [Range], y [RegularExpression] (que están implementados en ese namespace):

Nota: Arriva estamos indicando específicamente los mensajes de error como strings. Se pueden definir en archivos de recursos y localizarlos dependiendo del lenguaje/cultura del usuario. Podéis aprender cómo localizar los mensajes de error aquí

Ahora que hemos añadido los atributos de validación a nuestra clase Person, vamos a ejecutar la aplciación y ver qué ocurre cuando introducimos valores incorrectos y los mandamos al servidor:

Fijáos cómo la aplicación nos muestra una experiencia decente. Los elementos de textos con datos erroneos se resaltan en color rojo, y los mensajes de error se muestran al usuario. El formulario también mantiene los datos que el usuario introdujo originalmente - de manera que no tienen que volver a rellenarlos todos. Os preguntaréis ¿cómo a ocurrido todo esto?.

Para comprender este comprotamiento, miremos al método de acción Create que administra el escenario POST de nuestro formulario:

Cuando el formulario HTML vuelve al servidor, este método es llamado. Como el método de acción acepta objetos "Person" como parámetros, ASP.NET MVC creará un objeto Person y mapeará los datos de entrada a sus propiedades correspondientes. Como parte de este proceso, también comprobará los atributos de validación DataAnnotation para comprobar que está todo correcto. Si es así, el valor de ModelState.IsValid nos devolverá true - en ese caso guardaremos el objeto en base de datos y le redirigiremos a la página principal.

Si hay alguna validación con errores, nuestro método de acción repintará el formulario. Esto lo conseguimos con la última línea de código anterior.

Los mensajes de error se muestran en nuestra vista ya que el forumario Create tiene las llamadas al método <%= Html.ValidationMessageFor()%> junto al <%=Html.TextBoxFor()%>. El método Html.ValidationMessageFor() monstrará los mensajes de error apropiados para cualquier propiedad inválida a la vista:

Lo curioso sobre esta aproximación es que es realimente fácil de implementar - y nos permite añadir o cambiar fácilmente las reglas de validación de nuestra clase Person sin tener que cambiar el código de nuestros contorladores o vistas. Esta habilidad de indicar las reglas de validación en un lugar y que serán respetadas en cualquier parte permiten evolucionar nuestra aplicación y las reglas con un mínimo esfuerzo y mantienen nuestro código muy DRY

Paso 3: Habilitando la validación en cliente.

Por ahora nuestra aplicación tan sólo realiza validación en el lado del servidor - de esta manera los clientes necesitan hacer una petición al servidor ántes de que vean los errores de validación.

Una de las cosas chulas de la arquitectura de validación de ASP.NET MVC 2 es que soporta tanto validación en el servidor y en el cliente. Para habilitar esto, lo único que tenemos que hacer es añadir dos referencias JavaScript a nuestra vista y escribir una línea de código:

Cuando añadimos estas tres líneas, ASP.NET MVC 2 usará los metadatos de validación que hemos añadido a la clase Person y lanzara una lógica de validación JavaScript en el lado del cliente. Con esto, los usuarios veran inmediatamente los errores de validación cuando cambien el foco de un elemento de entrada.

Para ver el soporte de JavaScript en el cliente en acción de nuestra aplicación, volvemos a ejecutar nuestra aplicación y rellenaremos los primeros tres textbox con valores válidos -  luego intentaremos crearlo. Fijájos cómo obtenemos inmediatamente el mensaje de error para el valor que falta sin tener que llamar al servidor:

Si escribimos algún texto que no sea una dirección de email válida el mensaje de error cambiará inmediatamente de "Email Required" a "Not a valid email" (que son los mensajes de error que indicamos cuando añadimos las reglas a la clase Person):

Cuando introducimos un email válido el mensaje de error desaparece inmediatamente y el textbox volverá a su estado normal:

Lo bueno es que no tenemos que escribir ningún código JavaScript personalizado para permitir este escenario. Nuestro código de validación sigue siendo DRY - podemos especificar las reglas en un sólo lugar y nos aseguramos de que se cumplen en toda la aplicación - tanto en el cliente como en el servidor,

Fijáos que por razones de seguridad las relgas de validación de servidor se ejecutan siempre, aunque hayamos indicado el soporte de lado del cliente. Esto previene que hackers intenten spoofear nuestro servidor y se salten la seguridad del lado del Cliente.

El soporte de validación de JavaScript del lado del cliente de ASP.NET MVC 2 puede funcionar con cualquier framework/motor que usemos en ASP.NET MVC. No requiere que usemos la aproximación de DataAnnotation - toda la infraestructura funciona independientemente de DataAnnotations y puede funcionar con Castle Validator, el EntLib Validation Block, o cualqiuer solución personalizada de validación que usemos.

Si no queréis usar nuestro javascript de validación en el lado del cliente, podéis sustituir el plugin de validación de jQuery y usar esa librería en su lugar. La descarga de ASP.NET MVC Futures incluirá el soporte para habilitar la validación de jQuery sobre ASP.NET MVC 2 en el lado del servidor.

Paso 4: Crear un atributo de validación personalizado [email]

El namespace System.ComponentModel.DataAnnotations de .NET incluye un gran número de atributos de validación que podéis usar. Hemos usado 4 diferentes para el ejemplo anterior - [Required],[StringLength],[Range]  y [RegularExpresion]

También podéis definir vuestros atributos personalizados para poder usarlos. POdéis definir un atributo derivando de la clase ValidationAttribute del namespace System.ComponentModel.DataAnnotations. También podéis derivar de cualquier atributo ya existente si tan sólo queréis ampliar su comportamiento base.

Por ejemplo, para ayudar a limpiar el código de nuestra clase perosna podríamos crear un atributo [Email] que encapsule la expresión regular para comprobar los emails válidos. Para ello tan solo derivamos de la clase RegularExpression, y llamamos al constructor de la clase base con la expresion regular adecuada:

Podemos actualizar nuestra clase Person para que use nuestro nuevo atributo [Email], consiguiendo un código más limpio y encapsulado:

Cuando creamos atributos de validación personalizados podemos añadir lógica que se ejecuta tanto en el servidor y en el cliente a través de java script.

ADemás de crear atributos de validación que se aplican a propiedades individuales de un objeto, podemos crear atributos a nivel de clase - lo que nos permite crear validación sobre varias propiedades de un mismo objeto. Por ejemplo, podemos revisar el atributo personalizado "PropertiesMustMachAttribute" que se incluye en el archivo AccountModels.cs/vb de la plantilla de proyectos de ASP.NET MVC2 (tan sólo en FileNew ASP.NET MVC 2 Web Project en VS 2010 y veremos la clase).

Paso 5: Guardando en la base de datos

Vamos a implementar la logica necesaria para guardar a nuestros amigos en una base de datos.

Por ahora tan sólo estamos trabajando sobre una clase C# (también conocidas como clases "POCO" "plain old CLR (o C#)". Una aproximación que podemos usar es escribir algún código que mapee la clase que hemos escrito a una base de datos. Las soluciones de ORM como NHibernate soportan este mapping muy bien. El ADO.NET Entity Framework de .NET 4 también soporta esta característica, y como NHibernate también permiten esto tan sólo en código fuente, (sin archivos de mapeo).

Si nuestro objeto Person fué mapeado de esta manera no necesitaríesmo hacer ningún cambio a nuestra clase Person ni a las reglas de validación - seguirá funcionando correctamente.

¿Pero que pasa si queremjos usar una herramienta gráfica para nuestros mapeos ORM?

Muchos desarrolladores que usan Visual Studio hoy no escriben su lógica de ORM - sino que usan los diseñadores de Visual Studio para conseguir esto.

Una cuestión que surge cuando usamos DataAnnotations (o cualquier otro tipo de atributos) es "cómo las aplicamos cuando el modelo de objetos en el que estamos trabajando está creado con un diseñador". Por ejemplo, si en lugar de tener un objeto POCO de Person como la anterior, si la hemos definido con el diseñador de Visual Studio de LINQ to SQL o ADO .NET EF:

En la imagen anterior vemos como se define la clase Person con el diseñador  ADO.NET EF en VS 2010. La ventana superir define la clase Person, la ventana inferior muestra el mapeo de las propiedades a la tabla "People" de la base de datos. Cuando guardamos, el diseñador genera automáticamente la clase Person en nuestro proyecto. Esto es genial, exceptio que cada vez que cambiemos algo y guardemos se volverá a generar la clase Person - lo que hará que se pierda cualquier atributo de validación que hayamos indicado.

Una forma de aplicar atributos a clases que son generadas automáticamente por un diseñador de VS es emplear la técnica que conocmeos como "buddy classes". Básicamente creamos una clase separada que contiene nuestro atributos de validación y metadata, y luego las linkamos a la clase generada por el diseñador aplicando un atributo "MetadataType" a una clase parcial que es compilada con la clase generada. Por ejemplo, si queremos aplicar las relgas de validación que ehemos usado antes en la clase Person mantenida por los diseñadores de LINQ to SQL o ADO.NET EF podemos actualizar nuestro código de validación para que viva en la clase separada "Person_Validation" que es linkada a la clase "Person" creada por VS usando el código siguiente:

Esta aproximación no es elegante en una solución POCO pura - pero tiene la ventaja de funcionar con cualquier diseñador de VS.

Último paso: Guardar al amigo en la base de datos

El último paso - sin importar si usamos una clase POCO o generada por el diseñador - será guardar a nuestros amigos en la base de datos

Hacer esto tan solo requiere reemplacar el "Todo" del código de la clase FirendsController con 3 líneas de código que guarda el nuevo amigo en la base de datos. Aquí tenéis un código completo para la entidad FirendController class -cuando usamos ADO.NET EF para que guarde a la base de datos:

Y ahora cuando visitemos la url /Friends/Create podemos añadir nuevas Pople a nuestra base de datos:

La validcaicón de datos se asegura tanto en el lado del cliente como en el servidor. Podemos añadir/modificar/elimjinar fácilmente estas herramientas de validación en un sólo lugar, consiguendo que se cumplan todas esas reglas en todas las controladoras y en todas las vistas de nuestra aplicación.

Resúmen

ASP.NET MVC 2 hace mucho más fácil integrar validaciones en aplicaciones web. Propone validaciones basadas en modelos que nos permiten mantener nuestras aplicaciones DRY, y ayuda a que se cumplan esass reglas en toda la aplicación. El soporte incluido en ASP.NET MVC 2 hace que los escenarios más comunes de validación realmente sencillo. La posibilidad de extensión de esto permite soportar una gran variedad de escenarios más avanzados.

Espero que sirva.

Scott

Traducido por: Juan María Laó Ramos.

Artículo original.

13 pensamientos en “ASP.NET MVC 2: Validación de Modelos

  1. Pingback: ASP.NET MVC 2 « Thinking in .NET

  2. Gabriel

    Hola, lei el articulo, y te puedo decir que esta bastante bueno. gracias por la iniciativa.
    tengo un pequeño problema, y es que no puedo lograr que al agregar la vista, el view content me deje seleccionar la opcion create. Ya comprobe que tengo la clase person creada y guardada en los modelos, tambien probe crear la vista desde todos los actionresult disponibles.
    estoy usando VS2008 con C# codebeind. tendra algo que ver la version?
    saludos y gracias por la respuesta.
    Atte
    Gabriel F.

    Responder
  3. Zirkan

    He seguido el tutorial pero siempre en la depuración aparecen los siguientes errores:

    Error 1) La mejor coincidencia de método sobrecargado para ‘persona2.Models.DatabaseEntities.AddTotablaPersonas(persona2.Models.tablaPersona)’ tiene algunos argumentos no válidos
    Error 2 Argumento 1: no se puede convertir de ‘persona2.Models.Person’ a ‘persona2.Models.tablaPersona’.
    La clase se encuentra en la carpeta de modelos, el FriendController en la carpeta Controller y la vista está fuertemente tipeada con models.person (clase persona).
    La base de datos la cree en el mismo proyecto (app_data >> add >> newITem>> base de sqlServer) y ahi mismo agregué la tabla (id, firstName, LastName, Age & mail). En models agregué el Ado.Net EF (clic derecho >> agregar>>Nuevo Elemento>>Ado.net EF) a partir de la BD generada anteriormente y exportando la unica tabla y dejando como tal la entidad y el modelo que viene por default.
    ¿En que puede consistir el error? Please!!!! Help!!!

    Responder
  4. Carlos Enrique Cruz

    Esta muy buena tu informacion, me fue de gran ayuda pero tiene algunos errores antes mencionados en los comentarios pero en si esta m uy completa es nada mas de analisar y verificar algunos detalles

    Responder
  5. YAIR UNIVERSITARIO

    GRACIAS POR EL GRAN ESFUERZO KE ACES.
    MIRA TODO ESTO LO PROGRAME CON UN MAESTRO EN LA UNI.
    AHORA BIEN DESPUES DE TENER LA APLICACION WEB LISTO

    ¿COMO LO MONTO EN EL SERVIDOR IIS? ES UNA TAREA KE NOS DEJO EL MAESTRO PERO YA LE INTENTE MUCHO Y NADA ME MARCA ERRORES
    COMO 404 ERROR¡ ETC. ME GUSTARIA KE ME ENVIARAS LA
    INTALACION COMPLETA. GRACIAS POR SI SABES.

    Responder
  6. Cristopher

    Muchas gracias muy explicativo pero tengo un problema. ¿Como valido la entrada de un dato existente en la BD, es decir, como verifico que el amigo ingresado ya existe en la bD y muestro el mensaje de error?

    Responder

Deja un comentario