Server-Side vs Client-Side Validation in ASP.NET Core MVC
1. What is Validation?
Validation means checking whether the data entered by the user is correct before processing or saving it.
For example, suppose we have a registration form:
Name: [ Syed ]
Email: [ syed@gmail.com ]
Age: [ 25 ]
[ Register ]
We may have rules such as:
- Name is required.
- Email must be a valid email address.
- Age must be between 18 and 60.
ASP.NET Core MVC can perform these checks in two places:
VALIDATION
│
┌──────────┴──────────┐
│ │
Client-Side Server-Side
Validation Validation
│ │
Browser Server
2. Client-Side Validation
Simple Definition
Client-side validation checks user input in the browser before the form is sent to the server.
For example:
User enters invalid data
Age: 15
↓
Browser validates it
↓
"Age must be between 18 and 60"
↓
Request is NOT sent to server
This provides immediate feedback.
3. Server-Side Validation
Simple Definition
Server-side validation checks the submitted data on the server after the request reaches the MVC application.
Browser
│
│ POST /Employee/Create
▼
ASP.NET Core MVC
│
│ Model Binding
▼
Validation
│
▼
ModelState.IsValid
│
├── true → Process / Save
│
└── false → Return form with errors
Server-side validation is the authoritative validation because client-side validation can be bypassed.
4. Simple Example
Suppose we have an Employee form containing:
Name
Email
Age
We'll use Data Annotations.
No additional NuGet package is normally required for the basic MVC validation shown below; the validation attributes are provided by .NET, while the standard MVC project includes the client-side validation libraries needed for unobtrusive validation.
EmployeeViewModel
using System.ComponentModel.DataAnnotations;
public class EmployeeViewModel
{
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; } = string.Empty;
[Required(ErrorMessage = "Email is required")]
[EmailAddress(ErrorMessage = "Enter a valid email address")]
public string Email { get; set; } = string.Empty;
[Required]
[Range(18, 60, ErrorMessage = "Age must be between 18 and 60")]
public int Age { get; set; }
}
The important point is that the same validation rules can participate in both server-side and client-side validation.
For example:
[Required]
can be checked:
Browser → Client-side validation
AND
Server → Server-side validation
5. Controller
public class EmployeeController : Controller
{
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(EmployeeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Save employee to database
return RedirectToAction("Success");
}
public IActionResult Success()
{
return View();
}
}
The most important code here is:
if (!ModelState.IsValid)
{
return View(model);
}
6. View
Create.cshtml
@model EmployeeViewModel
<form asp-action="Create" method="post">
<div>
<label asp-for="Name"></label>
<input asp-for="Name" />
<span asp-validation-for="Name"></span>
</div>
<div>
<label asp-for="Email"></label>
<input asp-for="Email" />
<span asp-validation-for="Email"></span>
</div>
<div>
<label asp-for="Age"></label>
<input asp-for="Age" />
<span asp-validation-for="Age"></span>
</div>
<button type="submit">Save</button>
</form>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}
The following line is particularly important for the normal MVC client-side validation setup:
<partial name="_ValidationScriptsPartial" />
7. How Client-Side Validation Works
Suppose the user enters:
Name: Syed
Email: abc
Age: 15
Our model contains:
[EmailAddress]
public string Email { get; set; }
[Range(18, 60)]
public int Age { get; set; }
ASP.NET Core's tag helpers generate validation-related HTML metadata. Conceptually, the generated input contains information similar to:
<input
name="Age"
data-val="true"
data-val-range="Age must be between 18 and 60"
data-val-range-min="18"
data-val-range-max="60" />
The browser-side validation libraries read this metadata.
Typical MVC client-side validation uses:
jQuery
↓
jQuery Validation
↓
jQuery Validation Unobtrusive
↓
data-val-* attributes
↓
Validation message displayed
So when the user enters:
Age = 15
the browser can immediately show:
Age must be between 18 and 60
without making a normal form POST.
8. _ValidationScriptsPartial.cshtml
A standard MVC project commonly contains:
Views
└── Shared
└── _ValidationScriptsPartial.cshtml
It commonly loads scripts along these lines:
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
Then your page can include them through:
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}
Your layout must also render the section, commonly:
@await RenderSectionAsync("Scripts", required: false)
9. What Happens During Server-Side Validation?
Now suppose a request reaches the server.
POST /Employee/Create
Name=Syed
Email=abc
Age=15
ASP.NET Core MVC performs model binding and validation.
Conceptually:
HTTP Request
↓
Model Binding
↓
EmployeeViewModel created
↓
Validation attributes checked
↓
ModelState populated
↓
Controller Action
↓
ModelState.IsValid
For:
[EmailAddress]
public string Email { get; set; }
[Range(18, 60)]
public int Age { get; set; }
both values are invalid.
Therefore:
ModelState.IsValid
will be:
false
and this code runs:
if (!ModelState.IsValid)
{
return View(model);
}
The form is returned with validation errors.
10. What is ModelState?
ModelState stores information about model binding and validation errors.
For example:
if (!ModelState.IsValid)
{
return View(model);
}
Suppose:
Email = abc
Age = 15
Conceptually, ModelState contains:
Email
Error: Enter a valid email address
Age
Error: Age must be between 18 and 60
Therefore:
ModelState.IsValid == false
11. Very Important: Client Validation Can Be Bypassed
This is the biggest reason server-side validation is mandatory.
Suppose your website has client-side validation:
Age must be 18–60
A normal browser user enters:
Age = 15
The browser blocks submission.
But someone could bypass the UI completely and send an HTTP request using tools such as an API client or a custom script.
POST /Employee/Create
Name=Syed
Email=test@test.com
Age=15
The browser validation is now irrelevant because the request was constructed directly.
Therefore the server must validate the data again.
12. Why Use Client-Side Validation Then?
Because it improves user experience.
Without client-side validation:
Enter invalid value
↓
Submit
↓
Network request
↓
Server validation
↓
Server returns page
↓
Show error
With client-side validation:
Enter invalid value
↓
Browser validates
↓
Show error immediately
It is faster and avoids unnecessary round trips for ordinary user mistakes.
13. Why Server-Side Validation Is Mandatory
Server-side validation protects the application regardless of how the request was created.
Your MVC Form
│
▼
Server
▲
│
┌─────────┼─────────┐
│ │ │
Browser Script HTTP Client
All requests eventually have to pass the server's validation rules.
That is why:
Client-side validation is mainly for user experience. Server-side validation is required for correctness and security boundaries.
14. Server-Side Only Example
You can disable the client validation scripts simply by not loading them on the page.
For example, remove:
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}
Your model can still contain:
[Required]
[Range(18, 60)]
public int Age { get; set; }
and your controller still checks:
if (!ModelState.IsValid)
{
return View(model);
}
The user submits:
Age = 15
The request reaches the server, server-side validation fails, and the view is returned with the error.
So:
Client-side validation ❌
Server-side validation ✅
still works.
15. Client-Side Only Is Not Sufficient
Suppose you have browser validation but the server ignores validation results:
[HttpPost]
public IActionResult Create(EmployeeViewModel model)
{
// BAD: ModelState not checked
SaveEmployee(model);
return RedirectToAction("Success");
}
Someone bypasses the browser and sends:
Age = 10
The application could save the invalid value.
Therefore this pattern is dangerous:
Client validation only
↓
Browser bypassed
↓
Server accepts invalid data
↓
Database
16. Adding Validation Errors Manually
Sometimes validation cannot be represented conveniently with a simple annotation.
For example:
Employee email must not already exist.
This normally requires checking a database.
[HttpPost]
public async Task<IActionResult> Create(EmployeeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var exists = await _context.Employees
.AnyAsync(x => x.Email == model.Email);
if (exists)
{
ModelState.AddModelError(
nameof(model.Email),
"Email already exists.");
return View(model);
}
// Save employee
return RedirectToAction("Success");
}
Here:
ModelState.AddModelError(...)
manually adds a validation error.
This is server-side validation.
17. Field-Level vs Model-Level Errors
You can associate an error with a particular property:
ModelState.AddModelError(
"Email",
"Email already exists.");
Then:
<span asp-validation-for="Email"></span>
can display it.
You can also add a model-level error:
ModelState.AddModelError(
string.Empty,
"Unable to create employee.");
A validation summary can display such errors:
<div asp-validation-summary="ModelOnly"></div>
A common form therefore looks like:
<form asp-action="Create" method="post">
<div asp-validation-summary="ModelOnly"></div>
<label asp-for="Name"></label>
<input asp-for="Name" />
<span asp-validation-for="Name"></span>
<button type="submit">Save</button>
</form>
18. Data Annotations Used by Both
Common validation attributes include:
[Required]
[StringLength(100)]
[Range(18, 60)]
[EmailAddress]
[Phone]
[RegularExpression(...)]
Example:
public class EmployeeViewModel
{
[Required(ErrorMessage = "Name is required")]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[Required]
[EmailAddress(ErrorMessage = "Invalid email")]
public string Email { get; set; } = string.Empty;
[Range(18, 60)]
public int Age { get; set; }
}
Many built-in annotations integrate naturally with both MVC's server validation and its unobtrusive client-validation system.
19. Important Difference: Not Every Validation Rule Automatically Runs Client-Side
This is an important interview point.
Suppose you create a custom validation attribute:
public class AllowedEmailDomainAttribute : ValidationAttribute
{
protected override ValidationResult? IsValid(
object? value,
ValidationContext validationContext)
{
var email = value?.ToString();
if (email != null && email.EndsWith("@company.com"))
{
return ValidationResult.Success;
}
return new ValidationResult(
"Only company.com emails are allowed.");
}
}
Then:
[AllowedEmailDomain]
public string Email { get; set; }
This naturally provides server-side validation.
But custom client-side behavior requires additional integration, such as implementing a client validation adapter/interface or writing JavaScript.
So don't assume:
Custom validation attribute
=
Automatic client-side validation
20. Remote Validation
Sometimes you want to perform a server check while the user is still filling out the form.
For example:
Email: syed@test.com
↓ AJAX
Server checks database
↓
"Email already exists"
ASP.NET Core MVC supports remote validation patterns using RemoteAttribute.
Example:
[Remote(
action: "VerifyEmail",
controller: "Employee")]
public string Email { get; set; } = string.Empty;
Controller:
public async Task<IActionResult> VerifyEmail(string email)
{
bool exists = await _context.Employees
.AnyAsync(x => x.Email == email);
if (exists)
{
return Json("Email already exists.");
}
return Json(true);
}
Here the browser triggers a request to the server while validating the field.
However, you should still enforce the rule during the actual POST. Remote validation is a convenience for early feedback, not a replacement for authoritative server validation.
21. Complete Flow
Suppose the user enters:
Name = ""
Email = "abc"
Age = 15
The overall flow is:
USER
│
▼
MVC FORM
│
▼
Client Validation
│
┌────────┴────────┐
│ │
Invalid Valid
│ │
▼ ▼
Show errors HTTP POST
│
▼
Model Binding
│
▼
Server Validation
│
ModelState.IsValid
│
┌──────────┴──────────┐
│ │
false true
│ │
▼ ▼
Return View Business Logic
with errors │
▼
Database
22. Server-Side vs Client-Side Validation
| Feature | Client-Side | Server-Side |
|---|---|---|
| Runs in | Browser | Web server |
| Speed | Very fast | Requires request |
| Server round trip | Usually no | Yes |
| Immediate feedback | Yes | No |
| Can be bypassed | Yes | Not by bypassing browser UI |
| Security boundary | No | Yes |
Uses ModelState |
No | Yes |
| Data Annotations | Many supported | Yes |
| JavaScript required | Typically yes | No |
| Database validation | Usually requires server request | Yes |
| Mandatory | No | Yes |
23. Real-World Example
Consider a registration form:
Name
Email
Password
Confirm Password
Date of Birth
Client-side validation
Good candidates include:
Required fields
Email format
Password length
Confirm password
Basic range checks
These give the user immediate feedback.
Server-side validation
The server should still validate those rules and also handle rules requiring authoritative application state, such as:
Email already registered
Username already exists
Account eligibility
Business rules
Database constraints
Authorization-related conditions
For example:
Email: syed@gmail.com
Client:
"Looks like a valid email address."
Server:
"That email is already registered."
The client can check format locally.
The server checks the actual business state.
24. Validation vs Business Rules
This distinction is useful in larger applications.
Simple input validation:
[Required]
[EmailAddress]
[StringLength(100)]
answers questions such as:
Was a value provided?
Is the format correct?
Is the length acceptable?
Business validation may answer:
Does this customer already exist?
Does this employee have permission?
Is sufficient stock available?
Is this order currently cancellable?
Those rules generally belong in the appropriate server-side application/domain logic rather than relying on browser validation.
25. Important Security Point
Client-side validation should never be trusted as a security mechanism.
Think of it this way:
Client-side validation
=
Convenience for the user
Server-side validation
=
Protection of application rules/data
Even server-side validation is only one layer. For critical data integrity, database constraints are also valuable.
For example:
MVC Validation
↓
Business Rules
↓
Database Constraints
A unique email requirement might ultimately also be protected by a unique database index, because two simultaneous requests could otherwise pass an "email doesn't exist" check before either one commits.
26. Common Interview Question
Q: If we have client-side validation, why do we need server-side validation?
A strong answer:
Client-side validation improves user experience by detecting invalid input immediately in the browser, but it can be bypassed. Server-side validation validates the request after it reaches the application and must be treated as the authoritative validation before processing or saving data.
27. Another Interview Question
Q: Can Data Annotations perform both validations?
Yes, many standard Data Annotations can support both.
For example:
[Required]
[EmailAddress]
[Range(18, 60)]
ASP.NET Core MVC performs server-side validation, while its unobtrusive client-validation integration can expose supported rules to the browser.
But remember:
A custom server-side validation attribute does not automatically mean equivalent JavaScript/client-side validation exists.
28. Key Points
- Client-side validation runs in the browser.
- Server-side validation runs on the server.
- Client-side validation provides fast feedback and better UX.
- Server-side validation is mandatory because browser validation can be bypassed.
- ASP.NET Core MVC stores binding and validation errors in
ModelState. - Check:
if (!ModelState.IsValid)
{
return View(model);
}
- Standard Data Annotations include:
[Required]
[StringLength]
[Range]
[EmailAddress]
[RegularExpression]
- Client-side MVC validation commonly depends on:
<partial name="_ValidationScriptsPartial" />
asp-validation-fordisplays property-specific errors.asp-validation-summarydisplays validation summaries.- Use
ModelState.AddModelError()for additional server-side errors. - Database/business validation should be enforced on the server.
- Never rely only on client-side validation.
Easy way to remember
Client-side = Better user experience Server-side = Trusted validation Best practice = Use both