Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best Practices ViewModel Validation in ASP.NET MVC

I am using DataAnnotations to validate my ViewModel on client side with jquery.validate.unobtrusive and on server side in ASP.NET MVC application.

Not so long time ago, I figured out that I can write validation like this:

[Required(ErrorMessage = "{0} is required")] public string Name { get; set; } 

That way I can easily define some general strings in config or in resources and always use it in DataAnnotations. So it will be easier to change validation messages in my whole application in future.

Also I know that there is a FluentValidation library that allows to add validation rules to already existing ViewModel. I know that there is a problem with Add/Edit ViewModels that could have similar fields but different ValidationRules.

Another problem that comes from client validation is that html newly added to DOM (using ajax request) should be parsed to enable validation. This is how I do it:

$('#some-ajax-form').data('validator', null);  $.validator.unobtrusive.parse('#some-ajax-form'); 

So I have some questions:

  1. Is there some other useful practises that could help centralize all validation rules in application?
  2. What's is a best way to solve Add/Edit ViewModel Validation problem? Can I use DataAnnotations with FluentValidation or separate Add and Edit ViewModels still is a best option?
  3. Is there any better way to initialize validation on new DOM elements that received with ajax call other that I mention?

I'm not asking how to create my own DataValidators I know how to do it. I seeking of ways how to use them in more productive and easy maintainable way.

like image 747
teo van kot Avatar asked Jan 22 '15 13:01

teo van kot


People also ask

How do you handle validation in MVC?

In code we need to check the IsValid property of the ModelState object. If there is a validation error in any of the input fields then the IsValid property is set to false. If all the fields are satisfied then the IsValid property is set to true. Depending upon the value of the property, we need to write the code.

What exactly does ModelState IsValid do?

ModelState. IsValid indicates if it was possible to bind the incoming values from the request to the model correctly and whether any explicitly specified validation rules were broken during the model binding process.


1 Answers

To answer your 3th question first: No there is no easier way then what you are doing. Two lines of code to get it working can hardly be easier. Although there is a plug-in you could use, like explained in the question unobtrusive validation not working with dynamic content

Your first question, how to centralize validation, I normally use a separate class file to store all my validation rules. This way I don't have to browse through every single class file to find the rules, but have them all in one place. If that's better, is matter of choice. The main reason I started to use it, is to be able to add validation to auto-generated classes, like classes from the Entity Framework.

So I have a file called ModelValidation.cs in my data layer, and have code for all my models like

/// <summary> /// Validation rules for the <see cref="Test"/> object /// </summary> /// <remarks> /// 2015-01-26: Created /// </remarks> [MetadataType(typeof(TestValidation))] public partial class Test { } public class TestValidation {     /// <summary>Name is required</summary>     [Required]     [StringLength(100)]     public string Name { get; set; }      /// <summary>Text is multiline</summary>     [DataType(DataType.MultilineText)]     [AllowHtml]     public string Text { get; set; } } 

Now as you noticed I don't provide the actual error message. I use conventions by Haacked to add the messages. It makes it simple to add localized validation rules.

It basically comes down to a recource file containing something like:

Test_Name = "Provide name" Test_Name_Required = "Name is required" 

And these messages and naming will be used when you call regular MVC view code like

<div class="editor-container">     <div class="editor-label">         @Html.LabelFor(model => model.Name) <!--"Provide name"-->     </div>     <div class="editor-field">         @Html.EditorFor(model => model.Name)         @Html.ValidationMessageFor(model => model.Name) <!--"Name is required"-->     </div> </div> 

Your second question, about different validation for add/edit can be handled in two ways. The best way, would be to use views as they are actually intended. That means you don't pass your actual models to the views, but you create a view model that contains only the data. So you have a view model for Create with the proper validation rules and a view model for Edit with the proper rules, and when they pass you insert the result in your actual model. This however requires a lot more code and manual work, so I can imagine you're not really willing to do it like this.

Another option would be to use conditional validation like explained by viperguynaz. Now instead of a boolean, my classes that require a change between edit/add have a primary key Id int. So I check if Id>0 to determine if it is an edit or not.

UPDATE:

If you want to update validation on every ajax call, you could use jQuery ajaxComplete. This will revalidate all forms after every ajax request.

$( document ).ajaxComplete(function() {     $('form').each(function() {         var $el = $(this);         $el.data('validator', null);          $.validator.unobtrusive.parse($el);     }) }); 

If this is something you want, depends on how often you receive a form via AJAX. If you have a lot of AJAX request, like polling a status every 10seconds, than you don't want this. If you have an occasional AJAX request, that mostly contains a form, then you could use it.

If your AJAX returns a form you want to validate, then yes, it is good practise to update the validation. But I guess a better question would be "Do I really need to send the form by AJAX?" AJAX is fun and useful, but it should be used with care and thought.

like image 91
Hugo Delsing Avatar answered Oct 11 '22 14:10

Hugo Delsing