How to connect FluentValidator to web API?

I am trying to connect Fluent Validation to my Api MVC WEB project and it does not want to work.

When I use MyController : Controller works fine ( ModelState.IsValid returns False )

but when I use MyController :ApiController ... nothing.

Does anyone have any experience with how to connect them?

+30
c # asp.net-web-api fluentvalidation
Oct 19 '12 at 13:27
source share
6 answers

latest version of Fluent Validation (5.0.0.1) supports web api

Just install it from Nuget and register in Global.asax like this:

 using FluentValidation.Mvc.WebApi; public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { ... FluentValidationModelValidatorProvider.Configure(); } } 
+19
Jan 25 '14 at 12:36
source share

The response to this transfer request .

Basically you need to implement a custom ModelValidation Provider.

And a few more notes:

  • The web API does not work with modelValidator from the System.Web.Mvc namespace, only with those from System.Web.Http, as indicated here:

    Server side validation with custom DataAnnotationsModelValidatorProvider

  • You do not add it like this:

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

    But:

     GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Validation.ModelValidatorProvider), new WebApiFluentValidationModelValidatorProvider());` 
+14
Oct 24
source share

Since I was trying to solve this problem, I wanted to make sure that the same validator instance could be used for MVC and Web API. I was able to accomplish this by making two factories and using them together.

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; } } 

Factory API:

 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; } 

Then it turned out how to run a test for MVC and Web API. I ended up creating a wrapper for IValidator <>, which worked with the ModelValidator signature.

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

The last part was connecting validators to 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; 

Sorry, it was a little long, but hopefully this helps someone out.

+3
Feb 22 '13 at 15:02
source share

I found another simple solution for using FluentValidation in the Web API, but it lacks integration with ModelState and metadata. However, when creating an API that should not return the entire ModelState to the client (as required in MVC to rebuild the page), I found a trade-off for simplicity appropriate. Whenever the API input is invalid, I return a 400 Bad Request status code with a list of property identifiers and error messages. For this, I use a 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); } } } 

This attribute can be added as a global filter, individual controllers / actions, or a base class.

This code can certainly be improved, but it still helped me, so I wanted to make it available to others. Here are some of its shortcomings:

  • Zero inputs are not checked. I thought this would be more of a problem, but in practice, it’s just not so much (if at all) in our application. My controllers throw ArgumentNullExceptions for empty inputs that return 500 to the client, informing the client that the input cannot be null.
  • I can not use ModelState in my controllers. But after checking the required input is non-zero, I already know that ModelState is valid, so this can actually simplify the code. But it’s important that developers know that they don’t use it.
  • This implementation is currently hardcoded for the AttributedValidatorFactory. This should be abstracted, but so far it has been pretty low on my priority list.
+3
Feb 28 '13 at 15:53
source share

In WebApiConfig add two lines

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

Create a model and validator as follows:

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

This is about everything you need. Just write the controller as usual.

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

If you need a complete source code example, you can get it here - http://NoDogmaBlog.bryanhogan.net/2016/12/fluent-validation-with-web-api-2/

+2
Dec 06 '16 at 21:36
source share

The latest version of Fluent Validation does not support Mvc 4 or Web Api. Read this.

-four
Oct 20 '12 at 22:00
source share



All Articles