martes, 23 de junio de 2015

ASP.NET MVC. ModelValidatorProvider personalizado (I) - Creando el proyecto de prueba

El espacio de nombres System.ComponentModel.DataAnnotations nos proporciona diferentes atributos de validación con los que decorar las clases y propiedades del modelo. Estos atributos son interpretados por el framework de ASP.NET MVC para aplicar estas validaciones en nuestros controladores y vistas.

Sin embargo existen ocasiones en que estas validaciones no podemos definirlas de manera estática en la definición del modelo porque dependen de otras variables. Por ejemplo, ¿cómo definimos el rango de valores aceptados por un campo importe si el importe máximo depende del usuario que esté realizando la operación?

Por suerte la extensibilidad del framework de ASP.NET MVC nos permite crear nuestro propio proveedor de validaciones en el que podemos tener en cuenta circunstancias como ésta.

Para mostrar cómo podemos crear nuestro propio proveedor de validaciones voy a implementar un formulario de solicitudes de pedido en el que el importe máximo del artículo solicitado dependerá del usuario que lo solicita. Además, dependiendo del usuario que lo solicite, será obligatorio o no que especifique el motivo de la solicitud.

Este artículo voy a dedicarlo a crear el proyecto de ejemplo sobre el que implementar el proveedor de validaciones por lo que quien no esté interesado en seguir el ejemplo paso a paso puede pasar directamente al siguiente artículo (ASP.NET MVC. ModelValidatorProvider personalizado (y II) - Implementando el Proveedor) en el que creo el proveedor.

Creando el proyecto ejemplo

Así que voy a crear un nuevo proyecto de aplicación web CustomModelValidatorProvider y elegiré la plantilla de sitio vacío añadiendo las referencias a MVC.

Crear proyecto CustomModelValidatorProvider

Plantilla vacía con MVC

Como las validaciones a utilizar van a depender del usuario necesitaremos crear una autenticación básica. En el Web.config voy a habilitar la autenticación basada en formularios estableciendo una url para la página de login y estableciendo dos usuarios diferentes (Jefe y Empleado), para lo que crearé el elemento authentication dentro de system.web.

    <authentication mode="Forms">
      <forms loginUrl="~/Login">
        <credentials passwordFormat="Clear">
          <user name="Jefe" password="contra"/>
          <user name="Empleado" password="contra"/>
        </credentials>
      </forms>
    </authentication>

Por supuesto que jamás se me ocurriría implementar la autenticación de un sitio web en producción de esta forma y, por favor, que nadie siga este ejemplo para hacerlo. Pero para nuestro ejemplo nos servirá: tenemos dos usuarios  "Jefe" y "Empleado" (ambos con contraseña "contra") que nos permitirá diferenciar el comportamiento de la aplicación en función del usuario autenticado.

Necesitaremos también una página de login que nos permita validar el usuario y, si la validación es correcta, redirigirle al formulario de solicitud que tendremos en la página principal del sitio web.
Para ello voy a crear un modelo en un archivo Usuario.cs/Usuario.vb de la carpeta Model:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CustomModelValidatorProvider.Models
{
    public class Usuario
    {
        [Required]
        [Display(Name = "Usuario")]
        public string NombreUsuario { get; set; }
        [Required]
        [UIHint("Password")]
        [Display(Name = "Contraseña")]
        public string Contrasena { get; set; }
    }
}
Imports System.ComponentModel.DataAnnotations

Public Class Usuario
    <Required>
    <Display(Name:="Usuario")>
    Property NombreUsuario As String
    <Required>
    <UIHint("Password")>
    <Display(Name:="Contraseña")>
    Property Contrasena As String
End Class

Un controlador LoginController:

using CustomModelValidatorProvider.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;

namespace CustomModelValidatorProvider.Controllers
{
    public class LoginController : Controller
    {

        public ViewResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Index(Usuario usuario, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (FormsAuthentication.Authenticate(usuario.NombreUsuario, usuario.Contrasena))
                {
                    FormsAuthentication.SetAuthCookie(usuario.NombreUsuario, false);
                    return Redirect(returnUrl ?? Url.Action("Index", "Home"));
                }
                else
                {
                    ModelState.AddModelError("", "Usuario o contraseña incorrectos");
                    return View();
                }
            }
            else
                return View();

        }
    }
}
Imports System.Web.Mvc

Namespace Controllers
    Public Class LoginController
        Inherits Controller

        Function Index() As ViewResult
            Return View()
        End Function

        <HttpPost>
        Function Index(usuario As Usuario, returnUrl As String) As ActionResult
            If ModelState.IsValid Then
                If FormsAuthentication.Authenticate(usuario.NombreUsuario, usuario.Contrasena) Then
                    FormsAuthentication.SetAuthCookie(usuario.NombreUsuario, False)
                    If String.IsNullOrEmpty(returnUrl) Then
                        Return Redirect(Url.Action("Index", "Home"))
                    Else
                        Return Redirect(returnUrl)
                    End If
                Else
                    ModelState.AddModelError("", "Usuario o contraseña incorrectos")
                    Return View()
                End If
            Else
                Return View()
            End If
        End Function
    End Class
End Namespace

Y una vista para la acción Index del controlador:

Vista formulario login

@model CustomModelValidatorProvider.Models.Usuario

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div> 
        @using (Html.BeginForm())
        {
            @Html.ValidationSummary()
            @Html.EditorForModel()
            <br />
            <input type="submit" value="Validar" />
        }
    </div>
</body>
</html>
@ModelType CustomModelValidatorProvider.Usuario

@Code
    Layout = Nothing
End Code

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        @Using (Html.BeginForm())
            @Html.ValidationSummary()
            @Html.EditorForModel()
            @:<br />
            @:<input type="submit" value="Validar" />
        End Using
    </div>
</body>
</html>




A continuación voy a crear el formulario para realizar la solicitud de pedido. En primer lugar crearé un modelo muy simple compuesto por los campos código y descripción del artículo solicitado, el precio y el motivo por el que se realiza la solicitud. Así que crearé el archivo SolicitudPedido.cs/SolicitudPedido.vb en la carpeta Models.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CustomModelValidatorProvider.Models
{
    public class SolicitudPedido
    {
        [Display(Name = "Código de Artículo")]
        [Required]
        public string CodigoArticulo { get; set; }
        [Display(Name = "Descripción")]
        [Required]
        public string DescripcionArticulo { get; set; }
        [Required]
        public double Precio { get; set; }
        [Display(Name = "Motivo de la Solicitud")]
        public string Motivo { get; set; }
    }
}
Imports System.ComponentModel.DataAnnotations

Public Class SolicitudPedido
    <Display(Name:="Código de Artículo")>
    <Required>
    Property CodigoArticulo As String
    <Display(Name:="Descripción")>
    <Required>
    Property DescripcionArticulo As String
    <Required>
    Property Precio As Double
    <Display(Name:="Motivo de la Solicitud")>
    Property Motivo As String
End Class

Un controlador HomeController con dos acciones Index una para mostrar el formulario cuando la petición se realiza con el método HttpGet y otra para el método HttpPost para recuperar los datos de la solicitud y realizar la validación del modelo. El controlador lo he decorado con el atributo Authorize para que la aplicación se redirija al formulario de login si no se ha autenticado el usuario.

using CustomModelValidatorProvider.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace CustomModelValidatorProvider.Controllers
{

    [Authorize]
    public class HomeController : Controller
    {

        public ViewResult Index()
        {
            return View();
        }

        [HttpPost]
        public ViewResult Index(SolicitudPedido solicitud)
        {
            return View(solicitud);
        }
    }
}
Imports System.Web.Mvc

Namespace Controllers

    <Authorize>
    Public Class HomeController
        Inherits Controller

        Function Index() As ViewResult
            Return View()
        End Function

        <HttpPost>
        Function Index(solicitud As SolicitudPedido) As ViewResult
            Return View(solicitud)
        End Function
    End Class

End Namespace

Y finalmente la vista con el formulario para rellenar la solicitud que utiliza como modelo la clase SolicitudPedido.

Vista para la solicitud

@model CustomModelValidatorProvider.Models.SolicitudPedido

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        @using (Html.BeginForm())
        {
            @Html.EditorForModel()
            <input type="submit" value="Validar" />
        } 
    </div>
</body>
</html>
@ModelType CustomModelValidatorProvider.SolicitudPedido

@Code
    Layout = Nothing
End Code

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div> 
        @Using Html.BeginForm()
            @Html.EditorForModel()
            @:<input type="submit" value="Validar" />
        End Using

    </div>
</body>
</html>

Si arrancamos la aplicación nos redirigirá automáticamente al formulario de login, el cual nos permitirá validarnos con los usuarios "Jefe" o "Empleado" (ambos con la contraseña "contra"). Una vez validados la aplicación nos muestra el formulario  de solicitud de pedido en el que podemos comprobar, pulsando el botón "Validar", que se aplican las validaciones que hemos definido a través de los atributos del modelo.

Formulario de solicitud

Ya tenemos preparada la aplicación, pendiente de añadir las validaciones en función del usuario conectado.

En el siguiente artículo veremos cómo implementarlas creando nuestro propio proveedor de validadores.

Artículo siguiente:

ASP.NET MVC. ModelValidatorProvider personalizado (y II) - Implementando el Proveedor

El código

Puedes descargar el código de ejemplo de:


No hay comentarios:

Publicar un comentario