Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep validation DRY?

Using this approach to view models in MVC: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx

leaves an unanswered question in my mind. So it is about time I had it cleared up.

If I'm using auto mapper to map domain properties to a dto, then I appreciate that my domain layer can return a set of validation rules when the dto is mapped to a domain entity which is saved.

But, I don't see a DRY way of getting client validation to work and adding the errors to model state so they correspond to the correct property on the view model.

Cheers

like image 725
nick Avatar asked Jan 10 '11 11:01

nick


3 Answers

I found a related question with some interesting responses:

Mapping Validation Attributes From Domain Entity to DTO

I've been thinking about this, and in a way it is analogous to the situation we have with server-side and client-side validation. (e.g. Using both NHibernate Validator and jQuery.validate).

These days it's pretty well accepted that you should have a full set of server-side validation, and adding client-side validation is an option you can choose in order to make your application more user-friendly. It used to be that you had to implement your client-side validation manually, but you accepted the duplication of work because of the benefit in usability.

I'd argue that what we're dealing with here is very similar. You should have validation in your domain layer. You can't rely on the consuming applications to always add the validation themselves.

You then have the option in your application of adding validation on your DTO/view models. You do this because it's more helpful to deal with validation errors in the view rather than letting them get through to the domain which could throw an exception or give a less helpful error message. The point is that from the domain perspective you don't rely on this being done. You're still confident in your system because you know if any bad data does get through, your model will catch it.

The client/server case is a non-issue these days because so much work has been done to automate it, generating the client-side code from the server-side code (e.g. ModelValidatorProvider in ASP.Net MVC). I believe that as more and more people take up the use of view models/DTOs we'll start to see similar solutions for mapping domain validation onto the DTOs automatically (it's already happening with AutoMapper).

So in short, my (pragmatic rather than ideal ;)) answer is:

Accept the violation of DRY for now, do validation in both places, and try to contribute to projects that aim to automate it in future

like image 110
James Morcom Avatar answered Nov 15 '22 17:11

James Morcom


I prefer to put the Model as a property on the ViewModel along with other view-specific fields. That way it gets validated by MVC during binding and I can validate it on the backend when not being bound via MVC. This way the client validation gets provided by MVC and I get clean models with validation object not directly tied to a view.

public class MyViewModel
{
    public MyModel MyModel {get;set;}
    public bool IsSomethingAllowed {get;set;}
}

public class MyModel
{
    public int Id {get;set;}
    [Required]
    public string Name {get;set;}
}
like image 5
Jab Avatar answered Nov 15 '22 19:11

Jab


A common approach is to put all your validation in your view models, typically by using data annotations. MVC allows you to automatically generate client-side (javascript) validation from the data annotations. Pretty DRY.

You controller post actions take in the viewmodel and check the IsValid property. In this way, you are validating at client and server with the same code (or should, I say attributes in the case of data annotations):

[HttpPost]
public ActionResult ResetPassword(ResetPasswordViewModel viewModel)
{
  if (ModelState.IsValid)
  {
    // convert to dto/entity and pass to next layer
    // redirect to success page
    return RedirectToAction("ResetPasswordSuccess");
  }
  // display original view which will display error messages
  return View();
}

Just to add that just by having the controller action take in the viewModel as a parameter, the default MVC model binder will automatically validate your viewmodel and add any errors to the ModelState errors collection which is used to display errors within your views.

like image 2
Paul Hiles Avatar answered Nov 15 '22 18:11

Paul Hiles