📜 ⬆️ ⬇️

ASP.NET MVC: Conditional Validation on the client using FluentValidation

I was faced with the task of making conditional validation for a property of a model depending on the value of another property. Google says that such a task is quite common. So I decided to share how I decided it in my project.

The simplest option would be to write an attribute that, through reflection, would check the value of another property and the case in the hat. But this value was not universal, because even if you specify the name of the property you want to check, the type of the other property may still differ from model to model.

After some time spent searching for a beautiful solution, I came across the FluentValidation library. Its beauty is that with its help you can easily set checks for a model of almost any complexity, and if there are not enough opportunities, you can write your own validator and easily assign it to the desired property. The project site has very good documentation . Here is a small example of how this works:
')
[Validator(typeof(MyModelValidator))] public class MyModel { public string Name { get; set; } public string Description { get; set; } public bool IsDescriptionRequired { get; set; } } public class MyModelValidator: AbstractValidator<MyModel> { public MyModelValidator() { RuleFor(m => m.Name).NotEmpty(); RuleFor(m => m.Description).NotEmpty().When(m => m.IsDescriptionRequired); } } 


For simple conditions such as Filled, Minimum length, Maximum length, etc. client validation is implemented out of the box. To implement more complex checks (in my case conditional), you need to implement your property validator and implement the IClientValidatable interface, about which there is quite a bit of information .

The basic idea is to implement a method that returns a data set for jQuery.validate .

Instead of implementing a new class for each case when you need to implement validation on the client, I wanted to create a universal client validator, which can be passed a set of rules. It turned out something like this:

 using GetClientValidationRulesFunc = Func<ModelMetadata, ControllerContext, IEnumerable<ModelClientValidationRule>>; class ClientValidator : PropertyValidator, IClientValidatable { private readonly GetClientValidationRulesFunc _getClientValidationRulesFunc; public ClientValidator(GetClientValidationRulesFunc getClientValidationRulesFunc) : base((string)null) { _getClientValidationRulesFunc = getClientValidationRulesFunc; } protected override bool IsValid(PropertyValidatorContext context) { // Suppress any server side validation return true; } public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { return _getClientValidationRulesFunc(metadata, context); } } 


And you can use it like this:

 public class MyModelValidator: AbstractValidator<MyModel> { public MyModelValidator() { RuleFor(m => m.Name).NotEmpty(); RuleFor(m => m.Description).NotEmpty().When(m => m.IsDescriptionRequired).SetValidator(new ClientValidator(GetValidationRules)); } public IEnumerable<ModelClientValidationRule> GetValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new ModelClientValidationRule { ErrorMessage = "Description required", ValidationType = "validateDescription" }; } } 


Thus, you can maintain the flexibility of the library and separately set the rules for client validation. Another thing to say is that in order for the client to be validated, it is also necessary to create its own function and adapter for it:

 $.validator.unobtrusive.adapters.addBool("validateDescription"); $.validator.addMethod("validateDescription", function (value, element, param) { if ( $("#IsDescriptionRequired").val() === "true" ) { return $.trim($("#Description").val()).length > 0; } return true; }); 


The article turned out a bit crumpled and dry, but it is very difficult to reveal all the subtleties in one article. Perhaps it will be useful to someone, and if there is a need, I can tell you more about the details of interest.

You can download the package using NuGet: FluentValidation.Mvc3 with FluentValidation dependency

UPD:
In order for validation to work when the model is being bind, a validator provider must be registered with global.asax:

 protected void Application_Start(Object sender, EventArgs e) { FluentValidationModelValidatorProvider.Configure(); } 


And to the class for which the validator is created, you need to add an attribute that is in the FluentValidation.Attributes namespace:

 [FluentValidation.Attributes.Validator(typeof(MyModelValidator))] public class MyModel 

Source: https://habr.com/ru/post/145370/


All Articles