In this blog, we will implement remote validation by making an ajax call to the server without using jQuery; we will achieve this using vanilla JavaScript.
What is the purpose of remote validation?
Here’s an explanation: Often, user inputs must be checked against a database to see if it already exists. We need to validate the input on the client side. We must achieve this without posting the entire form to the server. In such a scenario, a server call is necessary to confirm the uniqueness of the entered data. A username, for example, must be unique when creating a new user.
Remote Validation
ASP.NET MVC 3 provides the mechanism for performing remote validation. ASP.NET MVC’s remote validation depends heavily on the jQuery and jQuery.Validate libraries. Remote validation makes an ajax call to the server using jQuery.

Let’s start with model.
Here’s my user model having Id, Username, and Password properties.
namespace ASP.Net_MVC.Models
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
publicint Id {get; set; }
[Required(ErrorMessage = "This username is required.")]
publicstring UserName {get; set; }
[Required(ErrorMessage = "This password is required.")]
publicstring Password {get; set; }
namespace ASP.Net_MVC.Models
{
[Table("Users")]
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "This username is required.")]
public string UserName { get; set; }
[Required(ErrorMessage = "This password is required.")]
public string Password { get; set; }
}
}
namespace ASP.Net_MVC.Models
{
[Table("Users")]
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "This username is required.")]
public string UserName { get; set; }
[Required(ErrorMessage = "This password is required.")]
public string Password { get; set; }
}
}
CheckExistingUser action method is called when a user clicks a create button.
namespace ASP.Net_MVC.Controllers
publicclass UserController : Controller
EmployeeContext _userService = newEmployeeContext();
public ActionResult Create()
public ActionResult Create(User user)
_userService.Users.Add(user);
_userService.SaveChanges();
returnRedirectToAction("Index");
[AcceptVerbs("GET", "POST")]
public ActionResult CheckExistingUser(string userName)
if(!_userService.Users.Any(u => u.UserName == userName))
returnnewHttpStatusCodeResult(200, $"A user {userName} already exists.");
namespace ASP.Net_MVC.Controllers
{
public class UserController : Controller
{
EmployeeContext _userService = new EmployeeContext();
// GET: User
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(User user)
{
if (ModelState.IsValid)
{
_userService.Users.Add(user);
_userService.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
[AcceptVerbs("GET", "POST")]
public ActionResult CheckExistingUser(string userName)
{
if (!_userService.Users.Any(u => u.UserName == userName))
{
return HttpNotFound();
}
return new HttpStatusCodeResult(200, $"A user {userName} already exists.");
}
namespace ASP.Net_MVC.Controllers
{
public class UserController : Controller
{
EmployeeContext _userService = new EmployeeContext();
// GET: User
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(User user)
{
if (ModelState.IsValid)
{
_userService.Users.Add(user);
_userService.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
[AcceptVerbs("GET", "POST")]
public ActionResult CheckExistingUser(string userName)
{
if (!_userService.Users.Any(u => u.UserName == userName))
{
return HttpNotFound();
}
return new HttpStatusCodeResult(200, $"A user {userName} already exists.");
}
Create.cshtml (View)
On form submission, JavaScript onsubmit event gets called, which in turn calls a JavaScript function validateForm(). We are embedding a URL to an action method by specifying the action and controller names in the Url.Action method().
@model ASP.Net_MVC.Models.User
ViewBag.Title = "Create User";
<formasp-controller="User"asp-action="Create"id="form-submit"method="post"
onsubmit="return validateForm('@Url.Action("CheckExistingUser","User", null)', event)">
<divclass="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
<labelid="errormessage"class="text-danger"></label>
<divclass="col-md-offset-2 col-md-10">
<inputtype="submit"value="Create"class="btn btn-default"/>
@Html.ActionLink("Back to List", "Index")
@model ASP.Net_MVC.Models.User
@{
ViewBag.Title = "Create User";
}
<form asp-controller="User" asp-action="Create" id="form-submit" method="post"
onsubmit="return validateForm('@Url.Action("CheckExistingUser","User", null)', event)">
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Create User</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="error-div">
<label id="errormessage" class="text-danger"></label>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@model ASP.Net_MVC.Models.User
@{
ViewBag.Title = "Create User";
}
<form asp-controller="User" asp-action="Create" id="form-submit" method="post"
onsubmit="return validateForm('@Url.Action("CheckExistingUser","User", null)', event)">
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Create User</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="error-div">
<label id="errormessage" class="text-danger"></label>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
@Html.ActionLink("Back to List", "Index")
</div>
JavaScript function to validate user
In the JavaScript validateForm() function, we split the URL and get the action method. We are splitting the URL as it can be reused for other action methods. We could get other DOM elements specific to each action method and reuse the same ajax call to send the fields to the action method and check for data uniqueness. From the controller action method, we receive a response, and based on that, we either display the validation error message to the user or submit the form.
If this method returns response code 404 NotFound, the username does not exist, and the form gets submitted; otherwise, the method returns response code 200 OK, a user already exists. The submitted form calls the create method, which adds the user to the database.
Note: The parameter name (userName) must match the parameter name in the appended query string of the URL in JavaScript.
functionvalidateForm(url, event){
let splits = url.split('/');
let action = splits[splits.length - 1];
if(action.includes("CheckExistingUser")){
let userName = document.getElementById("UserName").value
url += `?userName=${userName}`
var xhttp = newXMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.onreadystatechange = function(event){
if(this.readyState == 4&&this.status == 200){
document.getElementById("errormessage").innerHTML = this.statusText;
elseif(this.readyState == 4&&this.status == 404){
document.querySelector("#form-submit").submit();
function validateForm(url, event) {
event.preventDefault();
let splits = url.split('/');
let action = splits[splits.length - 1];
if (action.includes("CheckExistingUser")) {
console.log(action);
let userName = document.getElementById("UserName").value
url += `?userName=${userName}`
}
var xhttp = new XMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.send();
xhttp.onreadystatechange = function (event) {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("errormessage").innerHTML = this.statusText;
}
else if (this.readyState == 4 && this.status == 404) {
document.querySelector("#form-submit").submit();
}
};
}
function validateForm(url, event) {
event.preventDefault();
let splits = url.split('/');
let action = splits[splits.length - 1];
if (action.includes("CheckExistingUser")) {
console.log(action);
let userName = document.getElementById("UserName").value
url += `?userName=${userName}`
}
var xhttp = new XMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.send();
xhttp.onreadystatechange = function (event) {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("errormessage").innerHTML = this.statusText;
}
else if (this.readyState == 4 && this.status == 404) {
document.querySelector("#form-submit").submit();
}
};
}
The same ajax call is extended to include additional action methods. We can also check multiple fields on a form by retrieving them from the DOM and passing them via URL.
functionvalidateForm(url, event){
let splits = url.split('/');
let action = splits[splits.length - 1];
if(action.includes("CheckExistingUser")){
let userName = document.getElementById("UserName").value
let pswd = document.getElementById("Password").value
url += `?userName=${userName}&password=${pswd}`
elseif(action.includes("CheckExistingEmployee")){
let employeeId = document.getElementById("EmployeeId").value
let email = document.getElementById("Email").value
url += `?EmployeeId=${employeeId}&Email=${email}`
var xhttp = newXMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.onreadystatechange = function(event){
if(this.readyState == 4&&this.status == 200)
document.getElementById("errormessage").innerHTML = this.statusText;
elseif(this.readyState == 4&&this.status == 404){
document.querySelector("#form-submit").submit();
function validateForm(url, event) {
event.preventDefault();
let splits = url.split('/');
let action = splits[splits.length - 1];
if (action.includes("CheckExistingUser")) {
let userName = document.getElementById("UserName").value
let pswd = document.getElementById("Password").value
url += `?userName=${userName}&password=${pswd}`
}
else if (action.includes("CheckExistingEmployee")) {
let employeeId = document.getElementById("EmployeeId").value
let email = document.getElementById("Email").value
url += `?EmployeeId=${employeeId}&Email=${email}`
}
else {}
var xhttp = new XMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.send();
xhttp.onreadystatechange = function (event) {
if (this.readyState == 4 && this.status == 200)
{
document.getElementById("errormessage").innerHTML = this.statusText;
}
else if (this.readyState == 4 && this.status == 404) {
document.querySelector("#form-submit").submit();
}
};
}
function validateForm(url, event) {
event.preventDefault();
let splits = url.split('/');
let action = splits[splits.length - 1];
if (action.includes("CheckExistingUser")) {
let userName = document.getElementById("UserName").value
let pswd = document.getElementById("Password").value
url += `?userName=${userName}&password=${pswd}`
}
else if (action.includes("CheckExistingEmployee")) {
let employeeId = document.getElementById("EmployeeId").value
let email = document.getElementById("Email").value
url += `?EmployeeId=${employeeId}&Email=${email}`
}
else {}
var xhttp = new XMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.send();
xhttp.onreadystatechange = function (event) {
if (this.readyState == 4 && this.status == 200)
{
document.getElementById("errormessage").innerHTML = this.statusText;
}
else if (this.readyState == 4 && this.status == 404) {
document.querySelector("#form-submit").submit();
}
};
}
For the above scenario, the controller action method will look like this:
[AcceptVerbs("GET", "POST")]
public IActionResult CheckExistingEmployee(int EmployeeId, string Email)
if(!_employeeService.Employees.Any(u => u.EmployeeId == EmployeeId && u.Email == Email))
returnnewHttpStatusCodeResult(200, $"Employee {Email} already exists.");
[AcceptVerbs("GET", "POST")]
public IActionResult CheckExistingEmployee(int EmployeeId, string Email)
{
if (!_employeeService.Employees.Any(u => u.EmployeeId == EmployeeId && u.Email == Email))
{
return HttpNotFound();
}
return new HttpStatusCodeResult(200, $"Employee {Email} already exists.");
}
[AcceptVerbs("GET", "POST")]
public IActionResult CheckExistingEmployee(int EmployeeId, string Email)
{
if (!_employeeService.Employees.Any(u => u.EmployeeId == EmployeeId && u.Email == Email))
{
return HttpNotFound();
}
return new HttpStatusCodeResult(200, $"Employee {Email} already exists.");
}
Hope you will find this blog helpful. (By writing 20 -25 lines of JavaScript, we have avoided the use of jQuery and jQuery validate libraries)