Validation

Validation is one of the key operations for any app working with data. Before we persist the data to the persistent store (database/filesystem), we should ensure the sanctity of the data, its format, type, size, and check if it complies to our rules and doesn't pose any potential security threat. This is ensured through validation. This can be done at both the client and server side. I am a firm believer that validation should be performed at both the client and server side. The validation has been abstracted from the developers into validation attributes, which reduces the amount of code needed to perform the validation. Validation attributes are C# attributes that derive from ValidationAttribute. Most of the commonly used validation attributes can be found in the System.ComponentModel.DataAnnotations namespace. In case the already provided attribute does not suffice our needs, we can do either of the following:

  • Create a new custom validation attribute that derives from ValidationAttribute
  • Implement the interface IValidatableObject in our model class

The following code map diagram illustrates both the ValidationAttribute as well as the IValidatableObject interface:

IValidatableObject is a simple interface with just theValidate method, while ValidationAttribute has a lot more to offer for customization and has IsValid, Validate, and GetValidationResult methods along with properties to meet the validation needs. We also see that there are numerous attributes already defined and derived from ValidationAttribute. Some of the most important ones are:

  • UrlAttribute: Validates that the property has a valid URL.
  • PhoneAttribute: Validates that the property has a telephone format.
  • FileExtensionsAttribute: Validates that the file extensions are valid as per the predefined set of file extensions.
  • EmailAddressAttribute: Validates that the property has a valid email format.
  • CreditCardAttribute: Validates that the property has a credit card format.
  • DataTypeAttribute: Validates that the property has a valid data type, as specified by passing the DataType enumeration value. The DataType enumeration value can be seen at the top part of the preceding image.
  • RangeAttribute: Validates that the property value falls within the specified range.
  • RegularExpressionAttribute: Validates that the property value matches the given regular expression.
  • RequiredAttribute: Makes a property mandatory and hence the user would always have to provide its value.
  • CompareAttribute: Validates two properties in a model match.
  • StringLengthAttribute: Validates that the string property value length does not exceed the maximum length.

Applying only validation attributes to the model properties is not sufficient to perform the validation; we should ensure that on the server side in our action method, we also perform the ModelState.IsValid check to ensure that the input data validation passed or failed and act accordingly for pass and failed cases. The ASP.NET MVC Core framework sets the ModelState dictionary with errors after it performs the model binding from the HTTP request; if the validation does not pass, ModelState.IsValid would be false even if there is one validation error. This happens before the action execution starts, so we have the validation result available as soon as we enter the action method code. The framework, by default, continues validating properties until it reaches a maximum count of 200. This number is configurable though by writing the following code in the ConfigureServices method of the Startup.cs method, which sets the maximum count to 20:

services.AddMvc(options => options.MaxModelValidationErrors = 20);

In case we wish to perform some validations after the model binding and validations are completed, we can do so by manually invoking the validation using: TryValidateModel(<<model to validate>>).

All this time, what we saw was the server-side validation, which involves a round trip to the server and hence takes time, so to do an additional validation in the client side makes sense so that if the validation fails, we do not even send the request to the server. We will have a quick discussion about client-side validation when we look at the Views, later in the chapter. Now that we have the concept and theory in mind, let's do a quick implementation of the validation. Suppose we want to validate the book information, such as name, author, description, publisher, pages, release date, price, and so on, before saving the book information to the database. To do so, let's create a model and controller. The model would look like this:

public class Book
{
public int Id {get;set;}

[Required]
[StringLength(100)]
public string Name {get;set;}

[Required]
[StringLength(50)]
public string Author {get;set;}

[Required]
[StringLength(1000)]
public string Description {get;set;}

[Required]
[StringLength(50)]
public string Publisher {get;set;}

//// No point of having required attribute here as its a value type
and has a default value.
public int Pages {get;set;}

[DataType(DataType.Date)]
public DateTime ReleaseDate {get;set;}

[Range(0, 499.99)]
public decimal Price {get;set;}
}

And the controller would look like this:

public class BooksController: Controller
{
[HttpPost]
public IActionResult SaveBookInfo(Book model)
{
if(this.ModelState.IsValid)
{
/// Code for successful validation.
}
else
{
//// Code for failed validation.
return this.View(model);
}
}
}

As we can see, there are two paths in the SaveBookInfo method. If ModelState.IsValid is true, the data is valid and the data can be saved to the persistent storage; otherwise, we return to the same View. Generally, the View would have a validation summary that would display the validation errors.

That's it! It is this easy to implement server-side validation. This concludes our discussion on validation. Let's move on to filters.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset