martes, 23 de junio de 2015

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

Artículo anterior:
ASP.NET MVC. ModelValidatorProvider personalizado (I) - Creando el proyecto de prueba

Tras crear el proyecto de prueba en este artículo vamos a ver cómo crear un proveedor de validadores personalizado de forma que podamos incluir validaciones en función del usuario autenticado en la aplicación.

Para crear el proveedor de validaciones deberemos crear una clase que herede de la clase abstracta System.Web.Mvc.ModelValidatorProvider. Esta clase declara un único método GetValidators que devuelve una lista de objetos ModelValidator.

    // Resumen:
    //     Proporciona una lista de validadores para un modelo.
    public abstract class ModelValidatorProvider
    {
        // Resumen:
        //     Cuando se implementa en una clase derivada, inicializa una nueva instancia
        //     de la clase System.Web.Mvc.ModelValidatorProvider.
        protected ModelValidatorProvider();

        // Resumen:
        //     Obtiene una lista de validadores.
        //
        // Parámetros:
        //   metadata:
        //     Los metadatos.
        //
        //   context:
        //     Contexto.
        //
        // Devuelve:
        //     Lista de validadores.
        public abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context);
    }

Así que voy a crear en una nueva carpeta Infrastructure una nueva clase ValidatorProvider que herede de la clase ModelValidatorProvider.

El objetivo es que el proveedor devuelva una nueva validación para limitar el campo Precio de la clase SolicitudPedido en función del usuario conectado y, si el usuario no es "Jefe", hacer obligatorio el campo Motivo.

Para comprobar si debemos devolver los validadores el método GetValidators recibe dos parámetros:

  • Un objeto ModelMetadata con los metadatos del elemento a validar. Para saber si el elemento a validar se corresponde con las propiedades que buscamos podemos comprobar la propiedad Container que devuelve el objeto al que pertenece la propiedad y que deberá ser del tipo SolicitudPedido, y la propiedad PropertyName que devuelve el nombre de la propiedad a validar.
  • Un objeto ControllerContext con el contexto del controlador. A través de este objeto podremos obtener, por ejemplo, la identidad del usuario actual.
Para crear el validador a devolver cada atributo del namespace DataAnnotations tiene su correspondiente clase con sufijo Adapter que genera el validador a partir de los metadatos del elemento, el objeto de contexto del controlador y una instancia del atributo correspondiente.

using CustomModelValidatorProvider.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace CustomModelValidatorProvider.Infrastructure
{
    
    public class ValidatorProvider: ModelValidatorProvider
    {
        public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
        {
            List<ModelValidator> validators = new List<ModelValidator>();
            if (metadata.Container is SolicitudPedido)
            {
                switch (metadata.PropertyName)
                {
                    case "Precio":
                        double precioMaximo = 100;
                        if (context.HttpContext.User.Identity.Name == "Jefe")
                            precioMaximo = 1000;

                        RangeAttribute range = new RangeAttribute(0.01, precioMaximo);
                        validators.Add(new RangeAttributeAdapter(metadata, context, range));
                        break;
                    case "Motivo":
                        if (context.HttpContext.User.Identity.Name != "Jefe")
                            validators.Add(new RequiredAttributeAdapter(metadata, context, new RequiredAttribute()));
                        break;
                }
            }
            return validators;
        }
    }
}
Imports System.Web.Mvc
Imports System.ComponentModel.DataAnnotations

Public Class ValidatorProvider
    Inherits ModelValidatorProvider

    Public Overrides Function GetValidators(metadata As ModelMetadata, context As ControllerContext) As IEnumerable(Of ModelValidator)
        Dim validators As New List(Of ModelValidator)
        If TypeOf metadata.Container Is SolicitudPedido Then
            Select Case metadata.PropertyName
                Case "Precio"
                    Dim precioMaximo As Double = 100
                    If context.HttpContext.User.Identity.Name = "Jefe" Then
                        precioMaximo = 1000
                    End If
                    Dim range As New RangeAttribute(0.01, precioMaximo)
                    validators.Add(New RangeAttributeAdapter(metadata, context, range))
                Case "Motivo"
                    If context.HttpContext.User.Identity.Name <> "Jefe" Then
                        validators.Add(New RequiredAttributeAdapter(metadata, context, New RequiredAttribute()))
                    End If
            End Select
        End If
        Return validators
    End Function

End Class



Por último únicamente quedaría registrar el proveedor en la aplicación. El framework de MVC proporciona una clase estática ModelValidatorProviders con una propiedad Providers que contiene los proveedores de validaciones de la aplicación.

En el evento Application_Start del archivo Global.asax le añadiré una instancia del nuevo proveedor a la colección.

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            ModelValidatorProviders.Providers.Add(
                new CustomModelValidatorProvider.Infrastructure.ValidatorProvider());
        }
    Protected Sub Application_Start()
        AreaRegistration.RegisterAllAreas()
        RouteConfig.RegisterRoutes(RouteTable.Routes)

        ModelValidatorProviders.Providers.Add(New ValidatorProvider())
    End Sub

Ahora si arrancamos de nuevo la aplicación podemos comprobar que las validaciones del formulario de solicitud son diferentes dependiendo del usuario con el que nos validemos:

Validaciones del formulario de solicitud

El código

Puedes descargar el código de ejemplo de:


No hay comentarios:

Publicar un comentario