Validation on ViewModels in ASP.NET MVC

2019-03-09 10:42发布

问题:

Most of the tips on how to implement validation in ASP.NET MVC seem to center around the Model (either building service layers between model and controller or decorating properties of the model with validation attributes).

In my application I use ViewModels for all communication between the controllers and the views.

I have a ViewModel for my login page called 'LoginViewModel' with a property called 'EmailAddress'.

When the user enters their email address and clicks submit, this ViewModel is populated and sent to the controller, where the email address is validated.

It must be a valid email address, and the user must be from a domain that's registered with the system.

What would be a convenient way to add validation to this? Should I put the validation in the ViewModel itself? Or should it stay in the controller?

回答1:

"Should I put the validation in the ViewModel itself? Or should it stay in the controller" I agree with Robert but I would add a plug for additional automation.

If you look at a tool such as xVal, you can see that routine validation (e.g., required fields, numbers within ranges, strings matching regular expressions) can be automatically done by decorating fields of your data classes. In fact, xVal can automatically write the JavaScript for routine validations so that it is carried out client side. All without writing any code. Deeper validations (e.g. is this user a member of a domain registered in our database?) happen server-side inside the model layer itself.

Using the ViewModel idiom can pose some challenges to this scheme. My current approach is to embed my entity objects inside my view model, e.g.

public class Contact {
    [Required]
    string Name { get; set; }
}

public class ContactView {
    public Contact Contact { get; set; }
    public string SomeOtherViewProperty { get; set; }
}

and then in the controller, shallow validation happens when updating the model:

UpdateModel(contactViewModel.Contact, "Contact");

and the validations requiring more information or more complicated calculations happen inside the model layer itself.

Another approach is not to embed the entity object but just map individual fields between the two. I've recently become aware of a tool called AutoMapper which automatically links fields between domain and view model objects. It looks like it should support this validation method, though I haven't used it yet.



回答2:

The NerdDinner tutorials show the validation as occurring in your partial classes of the model (i.e. Linq to SQL or Entity Framework). But since you're using View Models, you can put the validation logic there.

The Validation Logic doesn't go in the controller. Rather, it is hooked from the controller with a checking property, i.e. ModelState.IsValid

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {

        try {
            dinner.HostedBy = "SomeUser";

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

Full details are here:

Building the Model
http://nerddinnerbook.s3.amazonaws.com/Part3.htm

and here:

ViewData and ViewModel
http://nerddinnerbook.s3.amazonaws.com/Part6.htm