Instructor-led online courses in ASP.NET Core, ASP.NET MVC, and ASP.NET Design Patterns. Read more...
Instructor-led online courses in ASP.NET Core, ASP.NET MVC, and ASP.NET Design Patterns. Conducted by Bipin Joshi on weekends. More details are available here.

Perform Remote Validation in ASP.NET Core

While performing model validation, at times you need to validate values entered in client side controls against server side data. Such a validation is called remote validation. As you might have guessed remote validation uses Ajax to validate the data. Thus validation is initiated on the client side but performed on the server side. The outcome of the validation process is sent back to the client so that the end user can be notified accordingly. In this article we will learn to implement remote validations in ASP.NET Core MVC and ASP.NET Core Razor Pages.

Have a look at the following figure that shows the sample data entry form we use in our example.

The form accepts CustomerID, CompanyName, ContactName, and Country values from the end user. In addition to typical validations such as required and string length we also want to restrict certain countries in the Country textbox. If a country value doesn't meet our criteria an error message is displayed to the user.

Ok. First of all let's create a model for our application.

 

public class Customer
{
    [Required]
    [StringLength(5,MinimumLength =5)]
    public string CustomerID { get; set; }
    [Required]
    [StringLength(40)]
    public string CompanyName { get; set; }
    [Required]
    [StringLength(40)]
    public string ContactName { get; set; }
    [Required]
    [Remote("ValidateCountry","Validator",
ErrorMessage ="Please enter a valid country.")]
    public string Country { get; set; }
}

The Customer model class consists of four public properties - CustomerID, CompanyName, ContactName, and Country. Notice that the Country property is decorated with [Remote] attribute. The [Remote] attribute allows you to perform remote validations as explained earlier. It requires a controller name and an action name. The action specified in the [Remote] attribute implements the validation logic. If you are using the same validation with multiple models, you can isolate the action in its own controller. In the preceding code ValidateCountry action of ValidatorController performs the required validation. The ErrorMessage property specifies an error message to be displayed to the user.

The ValidateCountry() action used by the [Remote] attribute is shown below :

public IActionResult ValidateCountry(string country)
{
    bool result;

    if (country == "USA" || country == "UK" 
|| country == "India")
    {
        result = true;
    }
    else
    {
        result = false;
    }
    return Json(result);
}

The ValidateCountry() action accepts country parameter. Remember that the name of this parameter must be the same as the model property you are validating. Inside, you need to implement the country validation logic. In this simple example, we simple ensure that the country is USA, UK, or India.

Since [Remote] attribute is going to make an Ajax call to ValidateCountry() action, we return a JsonResult object using the Json() method. The Json() method accepts a .NET object and converts it into its JSON equivalent. Here, a .NET boolean value (true / false) will be returned to the client in its JSON form.

We just completed our model and validation action. Now let's move our attention to the HomeController and its Index view that renders the customer data entry form.

The Index() and OnPost() actions of HomeController are quite straightforward and are shown below :

public IActionResult Index()
{
    return View();
}


[HttpPost]
public IActionResult OnPost(Customer model)
{
    if(ModelState.IsValid)
    {
        ViewData["Message"] = "Success !!!";
    }
    return View("Index", model);
}

The customer data entry form submits to the OnPost() action. Inside, we simply set a success message in the ViewData and return the Index view.

The markup of  the Index view that renders the data entry form in the browser is shown below :

@model Customer

<script src="~/scripts/jquery.js"></script>
<script src="~/scripts/jquery.validate.js"></script>
<script src="~/scripts/jquery.validate.unobtrusive.js"></script>

<h1>Add Customer</h1>

<form asp-controller="Home" asp-action="OnPost" 
method="post">
  <table cellpadding="10" border="1">
    <tr>
      <td>
       <label asp-for="CustomerID">Customer ID :</label>
      </td>
      <td>
       <input type="text" asp-for="CustomerID" />
       <span asp-validation-for="CustomerID" class="error">
</span>
      </td>
    </tr>
    <tr>
    <td>
     <label asp-for="CompanyName">Company Name :</label>
    </td>
    <td>
     <input type="text" asp-for="CompanyName" />
     <span asp-validation-for="CompanyName" class="error">
</span>
    </td>
  </tr>
  <tr>
    <td>
     <label asp-for="ContactName">Contact Name :</label>
    </td>
    <td>
     <input type="text" asp-for="ContactName" />
     <span asp-validation-for="ContactName" class="error">
</span>
    </td>
  </tr>
  <tr>
   <td>
    <label asp-for="Country">Country :</label>
    </td>
    <td>
     <input type="text" asp-for="Country" />
     <span asp-validation-for="Country" class="error">
</span>
    </td>
  </tr>
  <tr>
   <td colspan="2">
   <input type="submit" value="Submit" />
   </td>
  </tr>
 </table>
 <br />
 <div asp-validation-summary="All" class="error">
</div>
 <br />
 <h2>@ViewData["Message"]</h2>
</form>

Notice the code marked in bold letters. At the top we add a script reference to three jQuery libraries - jquery.js, jquery.validate.js, and jquery.validate.unobtrusive.js. These libraries are necessary for the remote validation to work. Since remote validation needs to make Ajax calls to the server, it uses functionality provided by these libraries. These libraries are to be placed inside the Scripts folder under wwwroot. You can also reference these libraries from a CDN if you so wish.

The <form> POSTs to the OnPost() action of the HomeController. The form also uses validation tag helper and validation summary tag helper to display the validation error messages. At the bottom of the page we also display the Message from the ViewData. 

To test the working, set a break point on the ValidateCountry() action and run the application. Try to enter some country other than USA, UK, and India. If all goes well you will see the error message displayed in the validation tag helper as well as the validation summary tag helper.

Validation based on multiple model properties

In the example we just developed, our validation logic involved only Country property. What if we want to validate a property based on its value as well as values of a few more properties? Luckily the [Remote] attribute provides a way. Suppose we want to validate the CompanyName property and our validation logic also involves the value of Country property.

Have a look at the modified Customer class :

public class Customer
{
    [Required]
    [StringLength(5,MinimumLength =5)]
    public string CustomerID { get; set; }
    [Required]
    [StringLength(40)]
    [Remote("ValidateCompanyName", "Validator", 
ErrorMessage = "Please enter a valid company name.", 
AdditionalFields ="Country")]
    public string CompanyName { get; set; }
    [Required]
    [StringLength(40)]
    public string ContactName { get; set; }
    [Required]
    [Remote("ValidateCountry","Validator",
ErrorMessage ="Please enter a valid country.")]
    public string Country { get; set; }
}

The CompanyName property is now decorated with [Remote] attribute. Notice the use of AdditionalFields property that specifies a comma separated list of dependent properties (is case we need only Country property). This time the ValidateCompanyName() action from the Validator controller performs the validation and is shown below :

public IActionResult ValidateCompanyName
(string companyname, string country)
{
    bool result = true;

    if (country == "India" && 
!companyname.EndsWith("Ltd."))
    {
        result = false;
    }

    if (country == "USA" && 
!companyname.EndsWith("LLC"))
    {
        result = false;
    }

    return Json(result);
}

Note how the ValidateCompanyName() action takes two parameters - one for the property being validated (companyname) and one for the dependent property )country). Since we set the AdditionalFields property of the [Remote] attribute those additional values are passed to the validation action.

Inside, we implement the validation logic that makes use of CompanyName as well as Country values. The action returns a boolean value as before.

To test our modification, run the application and try entering country as India and company name that doesn't end with Ltd. Doing so should flash an error message. The following figure shows a sample run of the validation :

 

Setting a custom validation message from the validation action

In the preceding examples the view displayed the validation error message as specified in the ErrorMessage property of the [Remote] attribute. Although this works well in most of the cases, at times you may need to change the error message based on your validation logic. You can do that by returning a string message to the client instead of boolean value. The following code fragment shows how :

public IActionResult ValidateCountry
(string country)
{
    bool result;
    string msg = "";

    if (country == "USA" || country == 
"UK" || country == "India")
    {
        return Json(true);
    }
    else
    {
        return Json("Invalid country. 
Valid values are USA, UK, and India.");
    }
}

Once this is done the validation tag helper and validation summary helper will use this message string as the error message. The following figure shows a sample run of the above code :

Remote validation in Razor Pages

If you observe the [Remote] attribute, it's clear that it needs a controller and an action for the sake of validation. There is no Razor Pages specific version (as yet) of the [Remote] attribute. However, you can still use the ValidatorController, ValidateCountry() and ValidateCompanyName() actions in the page model. The following page model class makes this clear :

public class IndexModel : PageModel
{

    [Required]
    [StringLength(5, MinimumLength = 5)]
    [BindProperty]
    public string CustomerID { get; set; }
    [Required]
    [StringLength(40)]
    [Remote("ValidateCompanyName", 
"Validator", ErrorMessage = 
"Please enter a valid company name.", 
AdditionalFields = "Country")]
    [BindProperty]
    public string CompanyName { get; set; }
    [Required]
    [StringLength(40)]
    [BindProperty]
    public string ContactName { get; set; }
    [Required]
    [Remote("ValidateCountry", "Validator", 
ErrorMessage = "Please enter a valid country.")]
    [BindProperty]
    public string Country { get; set; }


    public void OnGet()
    {
    }


    [HttpPost]
    public void OnPost(Customer model)
    {
        if (ModelState.IsValid)
        {
            ViewData["Message"] = "Success !!!";
        }
    }

As you can see, the CompanyName and Country page model properties are decorated with the [Remote] attribute in the same way as the model earlier.

That's it for now ! Keep coding !!


Bipin Joshi is a software consultant, trainer, author and spiritual yoga mentor having 23+ years of experience in software development. He teaches online training courses in ASP.NET Core, Angular, and Design Patterns to individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 03 September 2018


Tags : ASP.NET ASP.NET Core AJAX MVC .NET Framework C# JavaScript


Subscribe to our newsletter

Get monthly email updates about new articles, tutorials, code samples, and how-tos getting added to our knowledge base.