loyalflow
loyalflow

Reputation: 14879

Custom MVC4 validation based on a bool method

I have a method that validates a number, I don't have a regular expression for this as it is kind of complicated to create.

public bool IsRegistrationNumberValid(int number)
{
...

}

On my form, I have a textbox and I want to add validation for this column. How can I create a custom annotation or hook into the ModelState object to add an error message?

My POST controller action is like:

    [HttpPost]
    public ActionResult Create(UserRegistrationViewData model)
    {
        if (ModelState.IsValid)
        {
        ...
        }
    }

I'm not sure what options I have, can I just create a custom attribute to add to my model? And/Or should i just hook into the Model state and add the error message before I check for ModelState.IsValid?

Upvotes: 3

Views: 3396

Answers (1)

Benjamin Gale
Benjamin Gale

Reputation: 13177

There are a few approaches to this and the best one for you will depend on the following:

  • Where your IsRegistrationNumberValid method is located and whether the the logic be moved?
  • Are you validating the integrity of the user input or the domain (you should be checking both, but the validation for each will be in a different place)?
  • Personal preference.

The way I see it you have the following options available:

  1. Validate in your controllers action methods.
  2. Validate using the IValidatableObject interface.
  3. Use a custom ValidationAttribute.
  4. Validate in your service layer.

Option 1: Validate in your controller:

First of all you could simply validate the value in your controllers action method and update the ModelState like so:

[HttpPost]
public ActionResult Create(UserRegistrationViewData model)
{
    if (ModelState.IsValid)
    {
        if (!someObject.IsRegistrationNumberValid(model.value))
        {
            ModelState.AddModelError("PropertyName", "There is an error..");
            Return View()
        }
        else
        {
            // Carry out successful action here...
        }
    }
}

Option 2: use the IValidatableObject interface.

A second, much cleaner way is to implement the IValidatableObject interface on your viewModel so that you can move the logic out of the controller:

public class ViewModel : IValidatableObject
{
    public int Value { get; set; }

    IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
    {
        if (!staticClass.IsRegistrationNumberValid(this.Value))
        {
            yield return new ValidationResult("An error occured");
        }
}

Option 3: Create a custom validation attribute.

As mentioned you could create a custom validation attribute by deriving from ValidationAttribute as shown in this article.

The choice between the IvalidatableObject interface and a custom validation attribute is usually down to preference, however, one case where the IValidatableObject interface wins is when your validation depends on multiple properties (E.G. checking if one date is after another).

Option 4: Validate in your service layer.

Finally, if your validation is dependant on other information from a database you might want to take a look at this tutorial on validating with a service layer. The article is not perfect (the service and controller are a bit too tightly coupled) but is a good start and with a few modifications you can pass database validation errors (such as primary key violations) into your user interface in a very transparent and user-friendly way.

You will probably end up using a mixture of options 2, 3, and 4. You don't really want to be using the first option if possible as it makes your controller methods more complicated and makes it more difficult to reuse validation logic elsewhere.

My advice would be the following:

  • If you are validating the integrity of the user input (E.G. checking a date is in the correct format) use a mixture of the IValidatableObject interface and the ValidationAttribute classes.
  • If you are validating the integrity of the domain (ensuring no duplicate entities are entered, or that relationships between entities are defined) carry out the validation in the service layer. 

Upvotes: 10

Related Questions