tutorial español crear c# asp.net-web-api fluentvalidation

c# - español - ¿Cómo conectar FluentValidator a una API web?



web api rest c# (6)

Como estaba buscando resolver esto, quería hacerlo para que la misma instancia de validador pudiera usarse para MVC y Web API. Pude lograr esto haciendo dos fábricas y utilizándolas juntas.

MVC Factory:

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 Factory:

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 fue averiguar cómo ejecutar la validación para MVC y Web API. Terminé creando un contenedor para el IValidator <> que funcionaba con la firma de ModelValidator.

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 conectar los validadores en Global.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.

Intento unir Validación fluida a mi proyecto MVC WEB Api, y no quiere funcionar.

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

pero cuando uso MyController :ApiController ... nada.

¿Alguien tiene experiencia sobre cómo conectarlos?


En WebApiConfig agrega dos líneas

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

Cree 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(); } }

Eso es todo lo que necesitas. Simplemente escriba el controlador como lo haría normalmente.

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

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


He encontrado otra solución simple para usar FluentValidation en Web API, pero carece de integración con ModelState y Metadata. Sin embargo, cuando construyo una API que no necesita devolver todo el ModelState al cliente (como se necesita en MVC para reconstruir la página), he encontrado que la compensación por la simplicidad vale la pena. Cuando una entrada API no es válida, devuelvo un código de estado de 400 solicitudes incorrectas con una lista de identificadores de propiedad y mensajes de error. Para hacer esto, uso un ActionFilterAttribute simple:

[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 se puede agregar como un filtro global, a controladores / acciones individuales, o a una clase base.

Ciertamente, este código puede mejorarse, pero hasta ahora me ha ido bien, así que quise ponerlo a disposición de otros. Estas son algunas de sus deficiencias:

  1. Las entradas nulas no están validadas. Pensé que esto sería más un problema, pero en la práctica simplemente no sucede mucho (si es que lo hace) en nuestra aplicación. Mis controladores arrojan ArgumentNullExceptions para entradas nulas que devolverían 500 al cliente informando al cliente que la entrada no puede ser nula.
  2. No puedo usar ModelState en mis controladores. Pero, después de validar que las entradas requeridas no son nulas, ya sé que ModelState es válido, así que esto puede servir para simplificar el código. Pero es importante que los desarrolladores sepan que no deben usarlo.
  3. En este momento, esta implementación está codificada para AttributedValidatorFactory. Esto debería resumirse, pero hasta ahora ha sido bastante bajo en mi lista de prioridades.

La última versión de Fluent Validation (5.0.0.1) es compatible con la API web

Simplemente instálalo desde Nuget y regístralo en Global.asax de la siguiente manera:

using FluentValidation.Mvc.WebApi; public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { ... FluentValidationModelValidatorProvider.Configure(); } }


La última versión de Validación fluida no es compatible con Mvc 4 o Web Api. Lee this.


La respuesta está en esta solicitud de extracción .

Básicamente, debe implementar Proveedor de ModelValidation personalizado.

Y un par de cosas más a tener en cuenta:

  1. La API web no funciona con modelValidator desde el espacio de nombres System.Web.Mvc, solo con los de System.Web.Http como se indica aquí:

    Validación del lado del servidor con DataAnnotationsModelValidatorProvider personalizado

  2. No lo agregas así:

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

    PERO así:

    GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Validation.ModelValidatorProvider), new WebApiFluentValidationModelValidatorProvider());`