ASP. NET MVC Four Verification Programming Methods

  • 2021-07-02 23:55:11
  • OfStack

We can use four different programming modes to verify binding parameters.

1. Manually validate the parameters of the binding

When defining the specific Action method, it is undoubtedly the most direct programming way to manually verify the successfully bound parameters. Next, we demonstrate how to implement the parameter verification logic in the corresponding Action method through a simple example, and respond the error message to the client without passing the verification. In an ASP. NET MVC application, we defined the following Person class as the validated data type, and its Name, Gender and Age attributes represent a person's name, gender and age respectively.


public class Person 
{ 
  [DisplayName(" Name ")] 
  public string Name { get; set; } 
 
  [DisplayName(" Gender ")] 
  public string Gender { get; set; } 
 
  [DisplayName(" Age ")] 
  public int? Age { get; set; } 
} 

Next, we define the following HomeController. In the Action method Index for the GET request, we create an Person object and render it as an Model in the corresponding View. Another Index method that supports POST requests has a parameter of type Person. In this Action method, we first call Validate method to validate this input parameter. If the validation is successful (ModeState. IsValid attribute returns True), we return an ContentResult with the content of "input data passed validation", otherwise, this parameter is presented as Model in the corresponding View.


public class HomeController : Controller 
{ 
  [HttpGet] 
  public ActionResult Index() 
  { 
    return View(new Person()); 
  } 
 
  [HttpPost] 
  public ActionResult Index(Person person) 
  { 
    Validate(person); 
 
    if (!ModelState.IsValid) 
    { 
      return View(person); 
    } 
    else 
    { 
      return Content(" The input data passed the verification "); 
    } 
  } 
 
  private void Validate(Person person) 
  { 
    if (string.IsNullOrEmpty(person.Name)) 
    { 
      ModelState.AddModelError("Name", "'Name' Is a required field "); 
    } 
 
    if (string.IsNullOrEmpty(person.Gender)) 
    { 
      ModelState.AddModelError("Gender", "'Gender' Is a required field "); 
    } 
    else if (!new string[] { "M", "F" }.Any( 
      g => string.Compare(person.Gender, g, true) == 0)) 
    { 
      ModelState.AddModelError("Gender",  
      " Effective 'Gender' Must be 'M','F' Yeah 1"); 
    } 
 
    if (null == person.Age) 
    { 
      ModelState.AddModelError("Age", "'Age' Is a required field "); 
    } 
    else if (person.Age > 25 || person.Age < 18) 
    { 
      ModelState.AddModelError("Age", " Effective 'Age' Must be in the 18 To 25 Between one year old "); 
    } 
  } 
} 

As shown in the above code fragment, in Validate, we verify the three attributes of Person object as parameters one by one. If the provided data fails to pass the verification, we will call the AddModelError method of the current ModelState to convert the specified verification error message into ModelError and save it. The specific verification rules we adopt are as follows.

The Name, Gender, and Age properties of an Person object are required fields and cannot be Null (or an empty string).
The value of the Gender attribute representing gender must be "M" (Male) or "F" (Female), and the rest are invalid values.
The age represented by the Age attribute must be between 18 and 25 years old.
The following is the definition of the Action method Index corresponding to View, a strongly typed View of Model type Person that contains a form for editing staff information. We call HtmlHelper directly < TModel > EditorForModel is rendered in edit mode in the form as an Person object of Model.


@model Person 
<html> 
<head> 
  <title> Edit staff information </title> 
</head> 
<body> 
  @using (Html.BeginForm()) 
  {  
    @Html.EditorForModel() 
    <input type="submit" value=" Save "/> 
  } 
</body> 
</html> 

After running the program directly, a page for editing basic information of personnel will be presented. If we enter illegal data and submit it, the corresponding verification information will be presented in the form shown in Figure 1.

2. Use the ValidationAttribute feature

Defining validation logic and business logic for input parameters in the Action method is not a recommended programming approach. In most cases, the same data type has the same validation rules in different application scenarios. If we can associate the validation rules with the data type and let the framework itself implement data validation, then the final developer can focus more on the implementation of business logic. In fact, this is also the default programming method supported by the Model authentication system of ASP. NET MVC. When we define a data type, we can apply the corresponding ValidationAttribute feature to the type and its data members to define the default validation rules.

The "System. ComponentModel. DataAnnotations" namespace defines a series of specific ValidationAttribute attribute types, most of which can be applied directly to a custom data type attribute to validate the target data member. These predefined validation features are not the focus of this chapter, and we will give a general introduction to them in the next article.

General validation can be done with these predefined ValidationAttribute features listed above, but in many cases we need to create custom ValidationAttribute features to solve some special validation. For example, in the validation of Person object in the above demonstration example, we require that the value indicating gender specified by Gender attribute must be one of "M/m" and "F/f", so such validation has to be realized through custom ValidationAttribute features.

We define an DomainAttribute attribute for the validation rule "A value must be within the specified range". As shown in the following code snippet, DomainAttribute has one IEnumerable < string > The read-only property Values of type provides a list of valid values, which is initialized in the constructor. The specific validation implementation is in the overridden IsValid method. If the validated value is in this list, the validation is considered successful and True is returned. To provide a friendly error message, we override the method FormatErrorMessage.


[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,  AllowMultiple = false)] 
public class DomainAttribute : ValidationAttribute 
{ 
  public IEnumerable<string> Values { get; private set; } 
 
  public DomainAttribute(string value) 
  { 
    this.Values = new string[] { value }; 
  } 
 
  public DomainAttribute(params string[] values) 
  { 
    this.Values = values; 
  } 
 
  public override bool IsValid(object value) 
  { 
    if (null == value) 
    { 
      return true; 
    } 
    return this.Values.Any(item => value.ToString() == item); 
  } 
 
  public override string FormatErrorMessage(string name) 
  { 
    string[] values = this.Values.Select(value => string.Format("'{0}'", value)).ToArray(); 
    return string.Format(base.ErrorMessageString, name,string.Join(",",  values)); 
  } 
} 

Because ASP. NET MVC automatically extracts ValidationAttribute attributes applied to target parameter types or data members when binding parameters, And use them to validate the supplied data, so we no longer need to validate ourselves in the Action method as demonstrated in Example 1 above, but only need to apply the corresponding ValidationAttribute feature when defining the parameter type Person to associate the validation rules adopted with the corresponding data members.

The following is the definition of an Person type with the relevant ValidationAttribute attribute applied to the attribute member. RequiredAttribute is applied to all three attributes to define them as required data members, while DomainAttribute and RangeAttribute are applied to Gender and Age respectively to limit the range of valid attribute values.


public class Person 
{ 
  [DisplayName(" Name ")] 
  [Required(ErrorMessageResourceName = "Required",  ErrorMessageResourceType = typeof(Resources))] 
  public string Name { get; set; } 
 
  [DisplayName(" Gender ")] 
  [Required(ErrorMessageResourceName = "Required",  ErrorMessageResourceType = typeof(Resources))] 
  [Domain("M", "F", "m", "f", ErrorMessageResourceName = "Domain", ErrorMessageResourceType = typeof(Resources))] 
  public string Gender { get; set; } 
 
  [DisplayName(" Age ")] 
  [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources))] 
  [Range(18, 25, ErrorMessageResourceName = "Range", ErrorMessageResourceType = typeof(Resources))] 
  public int? Age { get; set; } 
} 

The error messages for the three ValidationAttribute features are defined in the project's default resource file (we can create this resource file by right-clicking the project in Solution Exploror and selecting the "Properties" option in the context menu to open the "Project Properties" object box. Finally, select the "Resources" Tab page in the dialog box, and create a resource file by clicking the link in the page. The specific definition is shown in Figure 2.

Because ASP. NET MVC automatically extracts the ValidationAttribute feature applied to the binding parameter type to perform automatic validation of the bound parameters, we do not need to manually validate the parameters in a specific Action method at all. As shown in the following code snippet, we no longer explicitly call the Validate method in the Action method Index, but running the program and submitting the form with illegal data input will still result in the output shown in Figure 1.


public class HomeController : Controller 
{ 
  // Other members  
  [HttpPost] 
  public ActionResult Index(Person person) 
  { 
    if (!ModelState.IsValid) 
    { 
      return View(person); 
    } 
    else 
    { 
      return Content(" The input data passed the verification "); 
    } 
  } 
} 

3. Enable data types to implement the IValidatableObject interface

In addition to defining validation rules directly on data types through the ValidationAttribute feature and allowing ASP. NET MVC to validate parameters accordingly during parameter binding, we can also define validation operations directly on data types. Since we implement the validation operation directly on the data types, which means that the corresponding data objects have the ability of "self-validation", let's call these data types "self-validation types". These self-validation types implement the interface IValidatableObject with the following definition, which is defined under the namespace "System. ComponentModel. DataAnnotations".


public interface IValidatableObject 
{ 
  IEnumerable<ValidationResult> Validate( ValidationContext validationContext); 
} 

As shown in the code snippet above, the IValidatableObject interface has a 1-only method, Validate, in which authentication against itself is implemented. For the data type Person defined in the above demonstration example, we can define it as a self-validating type in the following form.


public class Person: IValidatableObject 
{ 
  [DisplayName(" Name ")] 
  public string Name { get; set; } 
 
  [DisplayName(" Gender ")] 
  public string Gender { get; set; } 
 
  [DisplayName(" Age ")] 
  public int? Age { get; set; } 
 
  public IEnumerable<ValidationResult> Validate( ValidationContext validationContext) 
  { 
    Person person = validationContext.ObjectInstance as Person; 
    if (null == person) 
    { 
      yield break; 
    } 
    if(string.IsNullOrEmpty(person.Name)) 
    { 
      yield return new ValidationResult("'Name' Is a required field ", new string[]{"Name"}); 
    } 
 
    if (string.IsNullOrEmpty(person.Gender)) 
    { 
      yield return new ValidationResult("'Gender' Is a required field ", new string[] { "Gender" }); 
    } 
    else if (!new string[]{"M","F"}.Any( g=>string.Compare(person.Gender,g, true) == 0)) 
    { 
      yield return new ValidationResult(" Effective 'Gender' Must be 'M','F' Yeah 1",  new string[] { "Gender" }); 
    } 
 
    if (null == person.Age) 
    { 
      yield return new ValidationResult("'Age' Is a required field ",  new string[] { "Age" }); 
    } 
    else if (person.Age > 25 || person.Age < 18) 
    { 
      yield return new ValidationResult("'Age' Must be in the 18 To 25 Between one year old ",  new string[] { "Age" }); 
    }       
  } 
} 

As shown in the code snippet above, we let the Person type implement the IValidatableObject interface. In the implemented Validate method, we fetch the validated Person object from the validation context and validate its attribute members one by one. If the data member fails validation, we encapsulate the error message and the data member name (attribute name) with an ValidationResult object, which ultimately returns a collection of element type ValidationResult. Without making any changes to other code, we run the program directly and submit the form with illegal data, and still get the output shown in Figure 1.

4. Enable data types to implement the IDataErrorInfo interface

Above, we have data types that implement the IValidatableObject interface and define the specific validation logic in the implemented Validate method. Such types can be recognized by ASP. NET MVC, which will automatically call this method to validate the bound data object. Similar automated validation can be achieved if we implement the IDataErrorInfo interface for data types.

The IDataErrorInfo interface is defined under the "System. ComponentModel" namespace and provides a standard way to customize error messages. As shown in the following code snippet, IDataErrorInfo has two members, the read-only property Error is used to get error messages based on itself, and the read-only index is used to return error messages for the specified data member.


public interface IDataErrorInfo 
{ 
  string Error { get; } 
  string this[string columnName] { get; } 
} 

Also for the example demonstrated above, we have now redefined the data type Person that needs to be validated. As shown in the following code snippet, we have Person implement the IDataErrorInfo interface. In the implemented index, we treat the index parameter columnName as the attribute name, according to which we validate the corresponding attribute members according to the above rules, and return the corresponding error message if the validation fails. Without making any changes to other code, we run the program directly and submit the form with illegal data, and still get the output shown in Figure 1.


public class Person : IDataErrorInfo 
{ 
  [DisplayName(" Name ")] 
  public string Name { get; set; } 
 
  [DisplayName(" Gender ")] 
  public string Gender { get; set; } 
 
  [DisplayName(" Age ")] 
  public int? Age { get; set; } 
 
  [ScaffoldColumn(false)] 
  public string Error { get; private set; } 
 
  public string this[string columnName] 
  { 
    get  
    { 
      switch (columnName) 
      { 
        case "Name": 
          {  
            if(string.IsNullOrEmpty(this.Name)) 
            { 
              return "' Name ' Is a required field "; 
            } 
            return null; 
          } 
        case "Gender": 
          { 
            if (string.IsNullOrEmpty(this.Gender)) 
            { 
              return "' Gender ' Is a required field "; 
            } 
            else if (!new string[] { "M", "F" }.Any( 
              g => string.Compare(this.Gender, g, true) == 0)) 
            { 
              return "' Gender ' Must be 'M','F' Yeah 1"; 
            } 
            return null; 
          } 
        case "Age": 
          { 
            if (null == this.Age) 
            { 
              return "' Age ' Is a required field "; 
            } 
            else if (this.Age > 25 || this.Age < 18) 
            { 
              return "' Age ' Must be in the 18 To 25 Between one year old "; 
            } 
            return null; 
          } 
        default: return null; 
             
      } 
    } 
  } 
} 

The above is to use four different programming modes to verify the implementation code for binding parameters, hoping to help everyone learn.


Related articles: