简体   繁体   中英

ASP.NET MVC - Validate Form Input

I am new to ASP.NET MVC. I am trying to validate some user input. The model behind my view looks like this:

public class ViewModel
{
  [Required]
  public int? Minimum { get; set; }

  [Required]
  public int? Maximum { get; set; }
}

My View (.cshtml file) looks like this

@model Website.Models.ViewModel
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    ...
<div class="row">
  <div class="col-sm-6"><input class="form-control input-sm" id="Minimum" name="Minimum" type="text" /></div>                            
  <div class="col-sm-6"><input class="form-control input-sm" id="Maximum" name="Maximum" type="text" /></div>
</div>
   ...

<br />
<button type="submit" class="btn btn-primary">Save</button>
}

My controller, looks like this:

public class MyController 
{
  public ActionResult Edit(int? id)
  {
    var model = new ViewModel();
    return View(model);
  }

  [HttpPost]
  public ActionResult Edit(ViewModel model)
  {
    if (ModelState.IsValid) {
      var result = await model.SaveAsync();
      return RedirectToAction("Edit", new { id = result.DemandId, u = "true" });
    }
    return View(model);
  }
}

My code is validating whether the field is provided or not. However, I need to add some business rules. For instance, I need to ensure that Maximum is more than Minimum. If it is not, then I need to give the user an error. I intend to have even more complicated validation scenarios.

My question is, how should I handle these advanced validation scenarios in ASP.NET MVC?

If you are going to make a lot of validation, I can recommend digging into the Fluent Validation library.

Once installed, you'll need to configure the FluentValidationModelValidatorProvider (which lives in the FluentValidation.Mvc namespace) during the Application_Start event of your MVC application.

This enables Fluent Validation to hook into the ModelState and trigger on ModelState.IsValid etc.

protected void Application_Start() {

    /* Your other initializing code */

    FluentValidationModelValidatorProvider.Configure();
}

Then create your validator

public class ViewModelValidator : AbstractValidator<ViewModel> {

    public ViewModelValidator() {
        RuleFor(x => x.Minimum).NotNull();
        RuleFor(x => x.Maximum).NotNull.GreaterThan(x => x.Minimum)
    }

}

And hook it onto your ViewModel.

[Validator(typeof(ViewModelValidator))]
public class ViewModel
{
     [Required]
     public int? Minimum { get; set; }

     [Required]
     public int? Maximum { get; set; }
}

Here is a list of all inbuilt validation and its easy to create custom, database driving validation as well.

You need to derive your class from IValidatableObject, have a look at this useful article:

http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3

One approach is to have a method (either on the controller or a separate validator class which you feed the model to to validate).

This way you can do as much elaborate validation as you like (hitting databases / services / files etc without polluting your POCO View models)

For simplicities sake, lets do it inline to the controller:

[HttpPost]
public ActionResult Edit(ViewModel model)
{
    ValidateEditViewModel(model);
    if (ModelState.IsValid) {

Inside ValidateEditViewModel:

private void ValidateEditViewModel(ViewModel model){
    if(model.. // something is wrong)
    {
        // this sets ModelState.IsValid = false
        ModelState.AddModelError("< PropertyNameThatIsInError >", "The item is removed from your cart");  
    }
}

Even better is to have a validator class which is injected via DI into the controller and then used throughout (this is more testable and SOLID).

I tend to have a I< ControllerName >ControllerValidator class (so, for the Homecontroller, IHomeControllerValidator), which is implemented by a < Controllername >Validator (HomeValidator) concrete class and injected via unity / dependencyResolver on controller construction.

Works beautifully.

One final extension to this is to have a base controller class of < TValidator > which takes the validator class instance instance and stores it in a consistent place for all controllers that use this base class.

public class BaseController<TValidator> : Controller{
    protected TValidator validator;
    public BaseController(TValidator validator){
         this.validator = validator;
    }
}

so HomeController is

public class HomeController : BaseController<IHomeControllerValidator>
{
    public HomeController(IHomeControllerValidator validator) : base(validator)
    {
    }
}

HTH

Use IValidatableObject interface in your view model.

public class ViewModel : IValidatableObject
{
    [Required]
    public int? Minimum { get; set; }

    [Required]
    public int? Maximum { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();

        if (this.Minimum > this.Maximum)
        {
            results.Add(new ValidationResult("Maximum must be larger than Minimum"));
        }

        return results;
    }
}

This will force ModelState.IsValid to be false when Minimum > Maximum .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM