Estoy tratando de gancho Fluidez de Validación para mi MVC WEB Api proyecto, y no quiero trabajar.

Cuando yo uso MyController : Controller -> funciona bien (ModelState.IsValid devuelve False)

pero cuando uso MyController :ApiController … nada de nada.

¿Alguien tiene experiencia sobre cómo gancho de esos ?

InformationsquelleAutor Marty | 2012-10-19

6 Comentarios

  1. 19

    última versión de Fluidez de Validación (5.0.0.1) compatible con la api de web

    Sólo tienes que instalar desde Nuget y registrarlo en el Mundial.asax así:

    using FluentValidation.Mvc.WebApi;
    
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            ...
            FluentValidationModelValidatorProvider.Configure();
        }
    }
    • He enviado una sugerencia para extraer Web de la API de la aplicación de MVC 5 porque son totalmente ajenos. Va a ver lo que el autor de la respuesta.
    • Esto fue hecho nuget.org/packages/FluentValidation.WebAPI
  2. 14

    La respuesta está en este pull request.

    Básicamente, Usted necesita para implementar personalizado ModelValidation Proveedor.

    Y un par de cosas más a la nota:

    1. API Web no funcionan con modelValidator del Sistema.Web.Mvc espacio de nombres, sólo con los de Sistema.Web.Http como se señaló aquí:

      Del lado de servidor de validación personalizadas DataAnnotationsModelValidatorProvider

    2. No agregarlo como este:

      ModelValidatorProviders.Providers.Add(new WebApiFluentValidationModelValidatorProvider());`

      PERO como esto:

      GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Validation.ModelValidatorProvider), new WebApiFluentValidationModelValidatorProvider());`
    • Una cosa más a tener en cuenta: no hay manera de cómo invocar «Revalidar» o «TryValidate» en WebApi controlador. Para considerar si Usted necesita esta enganchando en el primer lugar.
    • Por favor paja que hay una web mejor integración con la api de la aplicación: fluentvalidation.codeplex.com/SourceControl/network/forks/… no se combinan sin embargo demasiado.
    • Sólo una observación. Si usted está usando un contenedor de DI como Ninject, el de arriba no funciona. En su lugar, debe agregar un enlace en yout DI config. Sería algo como lo siguiente: kernel.Bind<System.Web.Http.Validation.ModelValidatorProvider>().To<WebApiFluentValidationModelValidatorProvider>(); kernel.Bind<IValidatorFactory>().To<NinjectValidatorFactory>().InSingletonScope();
    • usted sería capaz de publicar un ejemplo de WebApiFluentValidationModelValidatorProvider?
    • Creo que es destinado a ser FluentValidation.WebApi.FluentValidationModelValidatorProvider
  3. 3

    He encontrado otra solución simple para el uso de FluentValidation en la API de Web, pero carece de la integración con el ModelState y Metadatos. Sin embargo, cuando la construcción de una API que no necesita devolver todo el ModelState para el cliente (como es necesario en MVC para la reconstrucción de la página), me he encontrado con el inconveniente de la simplicidad a la pena. Cada vez que una API de entrada no es válido, vuelvo a 400 Bad Request código de estado con una lista de los Identificadores de propiedad y mensajes de error. Para hacer esto, yo uso un simple ActionFilterAttribute:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class ValidateInputsAttribute : ActionFilterAttribute
    {
        private static readonly IValidatorFactory ValidatorFactory = new AttributedValidatorFactory();
    
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            base.OnActionExecuting(actionContext);
            var errors = new Dictionary<string, string>();
            foreach (KeyValuePair<string, object> arg in actionContext.ActionArguments.Where(a => a.Value != null))
            {
                var argType = arg.Value.GetType();
                IValidator validator = ValidatorFactory.GetValidator(argType);
                if (validator != null)
                {
                    var validationResult = validator.Validate(arg.Value);
                    foreach (ValidationFailure error in validationResult.Errors)
                    {
                        errors[error.PropertyName] = error.ErrorMessage;
                    }
                }
            }
            if (errors.Any())
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, errors);
            }
        }
    }

    Este atributo puede ser agregado como un filtro global, a los controladores de las acciones, o a una clase base.

    Este código, ciertamente, puede ser mejorado, pero me ha servido bien hasta ahora, así que yo quería hacer disponible a otros. Aquí están algunas de sus deficiencias:

    1. Null entradas no están validados. Yo pensé que esto iba a ser más de un problema, pero en la práctica simplemente no sucede mucho (si en absoluto) en nuestra aplicación. Mi controladores de lanzar ArgumentNullExceptions null entradas que volver a 500 para el cliente informa al cliente que la entrada no puede ser null.
    2. No puedo usar el ModelState en mis controladores. Pero, después de la validación de los insumos requeridos son no nulos, ya sé que el ModelState es válido, por lo que esta realidad puede servir para simplificar el código. Pero es importante que los devs para saber que no lo uso.
    3. Ahora mismo esta aplicación está codificado por el AttributedValidatorFactory. Esta debe ser extraída, pero ha sido bastante bajo en mi lista de prioridades hasta ahora.
    • Su aplicación se puede dividir/abstrae en algunas partes de realidad: para buscar por un validador (DI basado en contenedores de fábrica), la validación de la misma y procesamiento, validación de resultados (error de ajuste al contexto).
  4. 3

    Como yo estaba buscando para solucionar esto he querido hacerlo de modo que el mismo validador ejemplo podría ser utilizado para MVC y Web API. Yo era capaz de lograr esto mediante la realización de dos fábricas y el uso de ellos juntos.

    MVC Fábrica:

    public class MVCValidationFactory : ValidatorFactoryBase
    {
        private readonly IKernel _kernel;
    
        public MVCValidationFactory(IKernel kernel)
        {
            _kernel = kernel;
        }
    
        public override IValidator CreateInstance(Type validatorType)
        {
            var returnType = _kernel.TryGet(validatorType);
    
            return returnType as IValidator;
        }
    }

    API de Fábrica:

    public class WebAPIValidationFactory : ModelValidatorProvider
    {
        private readonly MVCValidationFactory _mvcValidationFactory;
    
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
    
        public WebAPIValidationFactory(MVCValidationFactory mvcValidationFactory)
        {
            _mvcValidationFactory = mvcValidationFactory;
        }
    
        public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders)
        {
            try
            {
                var type = GetType(metadata);
    
                if (type != null)
                {
                    var fluentValidator =
                        _mvcValidationFactory.CreateInstance(typeof(FluentValidation.IValidator<>).MakeGenericType(type));
    
                    if (fluentValidator != null)
                    {
                        yield return new FluentValidationModelValidator(validatorProviders, fluentValidator);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
    
            return new List<ModelValidator>();
        }
    
        private static Type GetType(ModelMetadata metadata)
        {
            return metadata.ContainerType != null ? metadata.ContainerType.UnderlyingSystemType : null;
        }

    El truco entonces era averiguar cómo ejecutar la validación por tanto MVC y Web API. Terminé la creación de un contenedor para la IValidator<> que trabajó con el ModelValidator firma.

    public class FluentValidationModelValidator : ModelValidator
    {
        public IValidator innerValidator { get; private set; }
    
        public FluentValidationModelValidator(
            IEnumerable<ModelValidatorProvider> validatorProviders, IValidator validator)
            : base(validatorProviders)
        {
            innerValidator = validator;
        }
    
        public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container)
        {
            if (InnerValidator != null && container != null)
            {
                var result = innerValidator.Validate(container);
    
                return GetResults(result);
            }
    
            return new List<ModelValidationResult>();
        }
    
        private static IEnumerable<ModelValidationResult> GetResults(FluentValidation.Results.ValidationResult result)
        {
            return result.Errors.Select(error =>
                new ModelValidationResult
                {
                    MemberName = error.PropertyName,
                    Message = error.ErrorMessage
                }));
        }
    }

    La última parte fue para el alambre hasta los validadores en el Mundial.asax:

    MVCValidationFactory mvcValidationFactory = new MVCValidationFactory(KernelProvider.Instance.GetKernel());
    
    GlobalConfiguration.Configuration.Services.Add(
        typeof(ModelValidatorProvider),
        new WebAPIValidationFactory(mvcValidationFactory));
    
    ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(mvcValidationFactory));
    
    DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

    Lo siento, este fue un poco largo, pero espero que ayude a alguien fuera.

    • Son capaces de invocar a «Validar» desde el controlador ? El problema para mí, era necesaria la validación para ser ejecutado en el centro de la «controlador MVC método» (e.x. POST). Para conectarte a la MVC de Fábrica no trabajan para la API, la causa se rechazaría el objeto de ser publicado sin entrar en el método (aunque en mi caso, el método podría asignar una parte del valor necesario para ser válida)
    • Su respuesta es súper impresionante, debo decir.
    • Esto resultó ser extremadamente más complicado y había algo realmente extraño comportamiento. Usted puede utilizar el estándar de Ninject de fábrica (en su ejemplo, la MVCFactory) por el paso de la fábrica en estos FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure( y FluentValidation.WebApi.FluentValidationModelValidatorProvider.Configure( – Entonces también el Filtro, solo reemplaza el Sistema.Net.Http onActionExecuting y que funciona tanto para MVC y WebAPI – lo Cual es bueno porque MVC6 sólo utiliza System.Net y el MVC namepspace ha sido casi erradicada
  5. 2

    En el WebApiConfig agregar dos líneas

    public static class WebApiConfig
    {
       public static void Register(HttpConfiguration config)
       {
           //snip...
           //Fluent Validation
           config.Filters.Add(new ValidateModelStateFilter());
           FluentValidationModelValidatorProvider.Configure(config);
       }
    }

    Crear un modelo y un validador de la siguiente manera –

    [Validator(typeof(PersonCreateRequestModelValidator))] 
    public class PersonCreateRequestModel
    {
        public Guid PersonId { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }
    }
    
    
    public class PersonCreateRequestModelValidator : AbstractValidator
    {
        //Simple validator that checks for values in Firstname and Lastname
        public PersonCreateRequestModelValidator()
        {
            RuleFor(r => r.Firstname).NotEmpty();
            RuleFor(r => r.Lastname).NotEmpty();
        }
    }

    Que es todo lo que necesitas. Sólo tiene que escribir el controlador como lo haría normalmente.

    public IHttpActionResult Post([FromBody]PersonCreateRequestModel requestModel)
    {
        //snip..
        //return Ok(some new id);
    }

    Si quieres un completo código fuente de ejemplo se puede obtener aquí – http://NoDogmaBlog.bryanhogan.net/2016/12/fluent-validation-with-web-api-2/

    • No estoy seguro de por qué, pero esto simplemente no funciona 🙁 aunque tengo el código, como lo demuestran… y yo sé lo que supone para el trabajo, pero simplemente no. Lo cual es frustrante. Tengo tanto MVC y WebAPI aunque.. así que supongo que debe ser un problema en alguna parte.
    • el blog tiene el código fuente completo. Descargar y probar que.
    • Terminé con algo más FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure y, a continuación, FluentValidation.WebApi.FluentValidationModelValidatorProvider.Configure sin complicadas fábricas y apoderados, y sólo uno FilterAttribute. No sé por qué fue tan difícil para mí encontrar que, pero su trabajo para mí, no en MVC y WebAPI con 3ish líneas de código 😀
  6. -4

    Última versión de Fluidez de Validación no admite Mvc 4 Web o Api.
    Leer este.

    • no es cierto. Verificación de nuget. Hay FV para MVC3, así como para MVC4

Dejar respuesta

Please enter your comment!
Please enter your name here